如何在 Scheme 中使用 reduce 和 curry 为列表中的所有项目创建求和函数?

How to create sum function for all items in list using reduce and curry in Scheme?

我正在玩 Scheme 我想 运行 像这样:

(reduce curry + '(1 2 3))

为什么这不起作用?我也试过 运行 这个:

((curry + 1) 2)
((curry (curry + 1) 2) 3)
((curry (curry (curry + 1) 2) 3) 4)

LIPS my Scheme implementation it works (see beta version REPL)。为什么这在 Kawa 或 Guile 中不起作用。我的实现不正确吗?我没有对函数调用进行偶数检查。函数总是被调用,是这个原因吗?

我的 curry 函数是用 JavaScript 编写的,但我很快尝试在 Kawa 中创建函数来测试它的实现,这可能比我的要好得多。

添加 check-arg 缺失函数后,我使用 SRFI-1 code on GitHub 中的右折叠代码。

我用的是这个咖喱版本:

(define curry (lambda (f arg1) (lambda (arg2) (f arg1 arg2))))

我认为对于简单的测试来说这应该没问题。

一个应该起作用的例子是 JavaScript:

function type(label, arg, type) {
  // some type checking so you know which function
  // throw exception and why
  var arg_type;
  if (arg instanceof Array) {
     arg_type = 'array';
  } else if (arg === null) {
     arg_type = 'null';
  } else {
     arg_type = typeof arg;
  }
  if (arg_type !== type) {
    throw new Error(`${label}: Expecting ${type} got ${arg_type}`);
  }
}
function curry(fn, ...init_args) {
    type('curry', fn, 'function');
    var len = fn.length;
    return function() {
        var args = init_args.slice();
        function call(...more_args) {
            args = args.concat(more_args);
            //console.log({fn, len, args});
            if (args.length >= len) {
                return fn.apply(this, args);
            } else {
                return call;
            }
        }
        return call.apply(this, arguments);
    };
}


function add(...args) {
  return args.reduce((a,b) => a + b);
}

console.log(curry(curry(curry(curry(add, 1), 2), 3), 4)());

console.log([1,2,3,4].reduce((a,b) => curry(a,b), add)());

相同的代码在我的 LIPS 中有效:

((--> #(1 2 3 4) (reduce (lambda (a b) (curry a b)) +)))
((--> #(1 2 3 4) (reduce (binary curry) +)))

注意: -->是对数组调用reduce的宏(Scheme中的向量是数组,-->调用方法)。需要 lambda 和二进制函数(受 Ramda 库启发),因为 Array::reduce 添加给定数组的第三个参数。

我的问题是为什么这在卡瓦不起作用,我做错了什么?

我需要补充一点:

(reduce curry + '(1 2 3))

在 LIPS 中也不起作用,我不确定为什么,但我主要询问标准 R7RS 方案。我想在标准方案中进行测试,看看我的实现是否正确。

编辑:咖喱的简单实现实际上是错误的。这是正确的实现:

(define (curry f . args)
   (lambda more-args
      (apply f (append args more-args))))

((curry (curry (curry + 1) 2) 3) 4)
;; ==> 10

这也是正确函数的简化版本,它应该在调用函数之前检查参数的长度,如果它小于原始函数它应该继续返回lambda(不确定你是否可以在Scheme中检查参数的长度) .以上代码适用于 Guile 和 Kawa。

所以另一个问题(仍然存在)是如何使 JavaScript 减少 Scheme 中的工作量?

EDIT2: 这是有效的函数:

(define (fld fn init l)
  (do ((l l (cdr l))
       (result init (fn result (car l))))
    ((null? l) result)))


((fld curry + '(1 2 3 4)))
;; ==> 10

在 Kawa 和 Guile 中测试。

您是否知道在 Scheme 中使用默认函数制作类似 fld 的标准方法(不定义新函数,curry 除外)?对我来说,reduce 应该如何工作,这就是它在 JavaScript.

中的工作方式

我尝试了不同的折叠组合,其中 none 有效。

我用 mit-scheme 尝试过,但我怀疑在其他版本的方案中 meaning of reduce 是相同的。

(reduce curry '+ (list 1 2 3 4))

等同于

(curry (curry 1 2) 3)

“标准”reduce 的定义表明 + 参数仅在列表为空时使用。

因此您需要重新定义 reduce 以便按照您的预期运行。

在 mit-scheme 中,您应该使用 fold-left 代替,如果您希望一直使用 + 运算符,还应柯里化 + 运算符。

在方案的其他版本中 它有效,因为 the + operator is also curried by default.

这是因为你这样定义 curry:

(define curry
  (lambda (f arg1)                           ;type 1
    (lambda (arg2)                           ;type 2
      (f arg1 arg2))))

递归应用它,您将应用 (curry (curry + 1) 2)(curry + 1) 必须+ 具有相同的类型(即 + 必须具有类型 2)。我现在在工作,我现在没有时间为你正确定义咖喱,但在接下来的日子里也许我会找到时间。

注意:在正确的定义中,您应该期望 FOLD-LEFT 的输出必须应用于初始元素(0 用于加法)。

在@alinsoar 的帮助下,我在 MIT Scheme docs

中发现了过时的 fold-left 定义

此代码适用于 Kawa:

(require 'list-lib) ;; import SFRI-1


;; this is equivalence of fold-left using fold from MIT Scheme docs
(define (fold-left proc knil list)
   (fold (lambda (acc elt) (proc elt acc)) knil list))


(define (curry f . args)
  "(curry f . args)

   simplified version of curry"
   (lambda more-args
      (apply f (append args more-args))))

(display ((fold-left curry + '(1 2 3 4))))
(newline)