【问题标题】:Point-free programming puzzle无点编程难题
【发布时间】:2020-03-03 08:47:40
【问题描述】:

我有一个用匿名函数编写的函数,它可以使用对象列表对一组 ID 进行水合,匹配 id 属性。

const objectsList = [{ id: 1, ... }, { id: 2, ... }]
ids.map(id => R.find(R.propEq('id', id), objectsList))

(我知道以这种方式查找对象很愚蠢——效率低下等等——让我们暂时忽略它。)

所以,我想做到这一点,但我被卡住了。这是最好的尝试:

ids.map(R.find(R.propEq('id', R.__), objects))

但这不好。我还尝试翻转参数的顺序并使用 compose,但这并没有产生正确的结果:

ids.map(R.compose(R.flip(R.find)(objects), R.propEq('id')))

这也不是骰子...任何 FP 向导都愿意帮助那些总是试图找出无点编程细微差别的人?

更新

感谢那些回答的人!同意,不应该迷恋无点编程;主要是我要求加深对 FP 和 Ramda 工具的理解。我同意所有发帖者的观点,即不得将无点编程优先于可读性。

通过这次我确实学到了更多关于 Ramda 的知识,希望其他人也能这样做。 :-)

【问题讨论】:

  • 但这不好”你的意思是“它不工作”?
  • @Bergi:不,“这是彻头彻尾的邪恶!” ;-)

标签: functional-programming ramda.js


【解决方案1】:

编辑

请注意,此处的某些文本基于使用此版本的另一个答案:innerJoin(useWith(equals, [prop('id'), identity]))。该答案已得到改进,不再使用此技术。但是这些 cmets 本身仍然有意义。


我建议你不要迷恋无点编程,只在提高可读性的地方使用它。

我发现 OriDrori 的这个答案变体更容易阅读:

const extractMatches = R.innerJoin ((o, id) => o.id === id)

const objectsList = [{ id: 1, x: 'a' }, { id: 2, x: 'b' }, { id: 3, x: 'c' }, { id: 4, x: 'd' }, { id: 5, x: 'e' }]
const ids = [1, 2, 4]

console .log (
  extractMatches (objectsList, ids)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

即使有一些没有使用 Ramda 重量级且有点不直观的 useWith 的无点版本,也很难比上面的单行代码更具可读性。

useWith 是在许多 Ramda 用户仍在使用 ES5(甚至 ES3!)时编写的。如今,在我看来,这似乎不是一个好的选择。它唯一增加的就是无点写的能力,而且通常它会掩盖一些东西。 (免责声明:我是 Ramda 的创始人之一,也是我的忠实粉丝,但并不是每个功能都得到同等的喜爱!)

【讨论】:

    【解决方案2】:

    编辑:正如 Scott 所指出的,我没有仔细阅读这个问题。这不是无点样式,我认为实现无点样式解决方案不是一个好主意。斯科特对 Ori Drori 答案的变体对我来说看起来不错

    我会重新定义问题:“给定一个对象列表,只保留那些 id 被列入白名单的对象”

    鉴于这些对象:

    const objects = [{id: 1, title: 'foo'}, {id: 2, title: 'bar'}];
    

    还有这个白名单:

    const whitelist = [2];
    

    那么你可以这样做:

    const inArray = flip(includes);
    filter(propSatisfies(inArray(whitelist), 'id'), objects);
    //=> [{"id": 2, "title": "bar"}]
    

    【讨论】:

    • 请注意,这个问题是专门关于做这个无点的。正如我在回答中提到的那样,我认为这不是一个好主意,但您可能需要在此处说明您不这样做。
    • @ScottSauyet 正确!并同意!
    【解决方案3】:

    您需要使用R.unary 将函数的元数限制为1,因为Array.map() 将3 个参数传递给回调(值、索引、原始数组)。 R.propEq 需要 3 个参数,传递给 maps 回调的值填充它们,调用函数,R.find 获取结果,而不是函数。

    const objectsList = [{ id: 1 }, { id: 2 }]
    const ids = [2]
    
    const fn = R.unary(R.compose(R.flip(R.find)(objectsList), R.propEq('id')))
    
    const result = ids.map(fn)
    
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

    如果你想抛弃Array.map()并创建一个无点函数,你可以使用R.innerJoin。要创建谓词,请使用翻转的R.propEq('id')

    const { innerJoin, flip, propEq } = R
    
    const fn = innerJoin(flip(propEq('id')))
    
    const objectsList = [{ id: 1 }, { id: 2 }]
    const result = fn(objectsList, [2])
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

    【讨论】:

    • 当然,可以使用R.map,它没有Array.prototype.map 的问题。这些解决方案中的任何一个似乎都比它们原本需要的更复杂,只是为了支持无点。这就是所要求的,所以它是完全合适的(赞成!)但它是那种让人怀疑过度依赖无点的代码。
    • 我找到了一个更好的解决方案,它不使用R.useWith。您可能希望在答案中包含前面的代码,因为关于 R.useWith 的段落现在缺少上下文。
    • 谢谢,Ori Drori。更新了我的答案。而且新版很不错。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多