【问题标题】:How to translate code from Lisp (MIT Schema) into JavaScript using Ramda?如何使用 Ramda 将代码从 Lisp(MIT Schema)翻译成 JavaScript?
【发布时间】:2019-06-05 08:38:19
【问题描述】:

我目前正在自学函数式编程。

我正在尝试翻译以下内容:

(define a 3)
(define b (+ a 1))

(* (cond ((> a b) a)
         ((< a b) b)
         (else -1))
   (+ a 1))

进入 JavaScript(使用 Ramda)。

可以使用嵌套三元组,但我喜欢使用 Ramda 的 cond 函数。这是我所做的:

const a = 3;
const b = 3 + 1;

cond([[() => a > b, () => a], [() => a < b, () => b], [T, () => -1]])() * (a + 1)

我遇到的问题是我必须使用这些函数(例如() =&gt; 3)而不仅仅是它们的值(例如a)。

有没有办法避免这些功能?或者在 JavaScript 中是否有另一种更好的方法(甚至可能没有 Rambda)?

我想避免使用ifforswitch 等语句。

解决此问题的另一种方法是使用:

import gt from "ramda/src/gt";
import lt from "ramda/src/lt";

const a = () => 3;
const b = () => a() + 1;


cond([[gt(a, b), a], [lt(a, b), b], [T, () => -1]])() * (a() + 1);

这使ab 变得复杂,因为它们总是必须被调用(参见a() + 1)。

编辑:

由于某种原因,我将ab 定义为函数的最后一个代码不起作用????

【问题讨论】:

    标签: javascript functional-programming lisp ramda.js mit-scheme


    【解决方案1】:

    Ramda 是自动柯里化的,因此您可以使用某些参数调用该函数,并返回一个新函数。例如:

    const { pipe, cond, gt, lt, T, always, identity, multiply } = R
    
    const a = 3
    const b = 3 + 1
    
    const fn = (a) => pipe(
      cond([
        [gt(a), always(a)],
        [lt(a), identity],
        [T, always(-1)]
      ]),
      multiply(a + 1)
    )
    
    const result = fn(a)(b)
    
    console.log(result)
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"&gt;&lt;/script&gt;

    【讨论】:

    • 非常感谢!
    • @J.Hesters:cond 接受一组函数对,每个函数具有相同的输入。每对中的第一个应该返回一个布尔值,第二个应该返回相关值。所以always 是必要的。 always(a) 等价于 () =&gt; a
    • @ScottSauyet 谢谢你的解释。
    • @J.Hesters - 不客气。我对代码进行了一些更新,使其更加无意义。除了 Scott 的出色解释 - 将 always 替换为 console.log,就可以看到你得到了什么。
    • @OriDrori 谢谢!从我的(可能是坏的)类类 JS 样式与语句切换到更具功能性的样式真是太奇怪了。你必须以完全不同的方式思考。也在与无点风格斗争,这就是为什么非常感谢你的回答??
    【解决方案2】:

    我认为您的工作有点误解。 cond 旨在为您提供功能。它实际上不是为为表达式创建标量值而设计的。它当然可以这样做,只需立即调用它生成的函数即可。但这不是重点。此外,它实际上并不意味着使用空函数调用(除了在默认条件下传递T 的情况。)。同样,您可以这样做,但这与工具的本质背道而驰。

    虽然 Ramda 从 LISP 风格和 ML 风格的函数式语言中汲取灵感,虽然我个人更喜欢前者,但 Ramda 更接近 ML 世界,尤其是 Haskell。因此,它旨在支持的主要活动是通过组合其他函数来构建函数。

    如果我要解决这个问题,我可能不会使用任何 Ramda,选择这样的东西:

    const larger = (a, b) => (a > b) ? a : (b > a) ? b : -1
    const foo = (a, b) => (a + 1) * larger(a, b)
    
    foo(3, 4) //=> 16
    foo(6, 3) //=> 42
    foo(3, 3) //=> -4
    

    或者,如果我不需要重用larger,我可以像这样内联它:

    const foo = (a, b) => (a + 1) * ((a > b) ? a : (b > a) ? b : -1)
    

    我当然可以在 Ramda 中以无点的方式写这个:

    const larger = cond([[gt, unapply(head)], [lt, unapply(last)], [T, always(-1)]])
    const foo = converge(multiply, [inc, larger])
    

    或者再次,我可能会内联 larger,或者将 unapply(head) 替换为 nthArg(0) 并将 unapply(last) 替换为 nthArg(1)

    但这些选项都不像原始选项那样可读。 Ramda 在这里没有添加任何我可以看到的内容。请注意,我是 Ramda 的忠实粉丝;我创办了这个图书馆,并且是它的主要作者之一。但我不认为它应该用于所有问题。

    【讨论】:

    • 感谢您的见解!你会推荐哪个图书馆?如果您想尽可能接近 lisp,您会推荐哪个?一般来说,你会为 JavaScript 中的 FP 推荐哪一个?
    • 也感谢您创建 Lambda!我最近发现了 FP,我正在阅读 this book 来学习它,但它是用 Lisp 编写的,我的主要语言是 JavaScript。对于如何使用 JavaScript 学习 FP,您有什么建议(除了大量练习之外)吗?
    • 我建议不需要库。我认为我建议的纯 JS 版本已经可读。如果你不喜欢嵌套的三元语句,这显然可以用if 语句代替。像 Ramda 这样的库补充了 vanilla JS。它并不是要取代它。
    • 我认为您正在阅读的 SICP 是目前最好的通用编程入门书籍。我对 JS 有两个强烈的建议。 Marijn Haverbeke 的Eloquent JavaScript 是对该语言的一个非常温和的介绍,并显示了 FP 和 OOP 代码。对 FP JS 的最佳介绍,因为现在最常用的是 Dr. Booleans Mostly Adequate Guide
    • 感谢您的推荐!
    【解决方案3】:

    您不必在 Scheme 中将所有内容包装在 thunk(即不带参数的函数)中的原因是,在您的情况下,cond 是一个扩展为嵌套 ifs 的宏:

    (if (> a b) a
      (if (< a b) b
        -1))
    

    所以不,如果您想避免三元运算符并将所有内容包装在 thunk 中,那么您在 vanilla JS 中没有太多选择。

    如果您不介意使用非标准 JS,您可以使用带有 Sweet.js 的宏来实现 cond...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-01
      • 2017-07-24
      • 2023-03-08
      • 2018-09-09
      • 2021-10-19
      • 1970-01-01
      相关资源
      最近更新 更多