在学习函数式编程, Ramda 的一些疑问

2022-12-05 12:26:32 +08:00
 witcat

本人菜鸟...之前没特意研究过函数式编程
最近再做一个三消游戏,写了一点 Ramda ,我发现一个问题
下面这段大概意思是根据 row.length 判断当前这个格子需不需要填充砖块,根据结果返回砖块值或 null
最后 prepend 到 map 里

compose(
	prepend,
    ifElse(identity<boolean>, brickSample, always(null)),
    shouldFill
)(row.length)(row),

问题是我写了很多经常出现(arg)(map)这样的代码
直觉这样写不对,因为我看别人的代码没有这么写过,也许根据入参获取砖块值是一个流程,prepend 到地图里是另外一个流程
就又改成这样了:

prepend(
    compose(
        ifElse(identity<boolean>, brickSample, always(null)),
        shouldFill
    )(row.length),
    row
),

但是还是感觉很乱,有更好看的写法吗?

2312 次点击
所在节点    JavaScript
9 条回复
witcat
2022-12-05 13:07:19 +08:00
突然想起来可以用 copilot ,去充了一个,对学习很有帮助。
贴两个 copilot 写的 😂
```
ifElse(
shouldFill,
compose(prepend, brickSample),
compose(prepend, always(null))
)(row.length)(row),
```
```
prepend(
ifElse(shouldFill, brickSample, always(null))(row.length),
row
),
```
lmshl
2022-12-05 13:35:10 +08:00
楼主的用法真的是辣眼睛。

关于 JS 函数式编程,前两天我写了个知乎回答,贴过来 https://www.zhihu.com/question/570165038/answer/2784350974

Leviathann
2022-12-05 13:44:25 +08:00
point free 就是要把代码写成文章
如果无法从语义上(也就是函数名)表达代码在做什么那就是没写好,或者说这段代码不适合 point free
wanacry
2022-12-05 13:46:44 +08:00
关于 Ramda 的写法,它提供了一些函数组合的工具函数,例如 compose 、pipe 等,可以用来把多个函数组合在一起,并且从右往左依次执行。

对于上面的代码,可以把两个部分分别写成两个函数,然后用 compose 组合在一起,代码如下:

const getBrick = compose(
ifElse(identity<boolean>, brickSample, always(null)),
shouldFill
)

const prependBrick = (map) => (row) => prepend(getBrick(row.length)(row), map)

prependBrick(map)(row)

这样就可以很清晰地看出来,前面的部分是根据 row.length 获取砖块值,后面的部分是 prepend 到地图里。

当然,这个写法并不是必须的,你可以按照自己的喜好写,只要保证代码可读性即可。
witcat
2022-12-05 13:48:40 +08:00
@lmshl 看不懂你写的文 同样的功能你能不能改一个 让我看看不辣的函数式 js 是什么样的
lmshl
2022-12-05 13:54:46 +08:00
不辣的
```javascript
if (!shouldFill(row.length)) return row

const arr = brickSample()
return prepend(row, arr)
```
novolunt
2022-12-05 14:22:39 +08:00
// Define named functions for each part of the code.
function shouldFill(length: number) {
// Determine whether the current cell should be filled with a brick.
return length > 0;
}

function brickSample(filled: boolean) {
// If the cell should be filled, return the value of a brick.
// Otherwise, return null.
return filled ? BRICK_VALUE : null;
}

function prependBrick(brick: any, row: any[]) {
// Prepend the given brick to the given row.
return [brick, ...row];
}

// Use the named functions to create a new, composed function.
const fillRow = compose(
prependBrick,
brickSample,
shouldFill
);

// Use the composed function to fill the cells in a row.
const filledRow = fillRow(row.length, row);
cnhongwei
2022-12-05 14:54:07 +08:00
开眼了,感觉 Ramda 真的是函数式。但程序变得好神奇。
看了之后,还是坚定的自己的习惯,保持数据不可变,用好 map flatmap filter reduce 就可以好好的函数式编程了。
libook
2022-12-05 17:50:07 +08:00
做了 10 年 JS 全栈开发,表示两段看起来都很难读懂。

一般 JS 开发者的思维都是 foo() 或 foo=() 这种形式,即函数名后紧跟参数表的形式,所以看到 ()()() 这种形式就需要转换思维,先在脑内模拟运行前面的部分才能知道返回了什么函数来使用后面的参数表,在代码特别长的时候,单拎出来中间一部分可能无法确定是在执行什么函数,可能降低了代码阅读理解的效率。工业上来讲,个人认为 a().b().c() 这样链式调用的形式可能更明确。

通常,函数调用和逻辑判断流程有明显的语法格式差异,可以帮助阅读者快速切换这两种语境,但当流程控制也被封装成函数的时候,就需要人去了解函数内部的流程特征,个人认为不如直接在相关代码附近喂给阅读者要方便,在各个函数声明的区域跳来跳去也比较打断思路。

或许题主只想做一个使用 JS 实现的完全函数式编程案例,尽管这不是工业上最普遍的风格;这样的话题主就不要纠结好看不好看了,符合教材范式和核心目标就好吧。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/900181

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX