0

我正在玩 Scheme 我想运行这样的东西:

(reduce curry + '(1 2 3))

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

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

LIPS 我的 Scheme 实现中它可以工作(参见beta 版 REPL)。为什么这在 Kawa 或 Guile 中不起作用。我的实现不正确吗?我没有对函数调用进行数量检查。函数总是被调用,是这个原因吗?

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

添加缺少的功能后,我使用GitHub 上的 SRFI-1 代码中的右折叠代码。check-arg

我使用了这个咖喱版本:

(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 方案。我想在标准方案中进行测试,看看我的实现是否正确。

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

(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 中的工作方式。

我尝试了不同的折叠组合,但它们都不起作用。

4

2 回答 2

1

我试过了,mit-scheme但我怀疑在其他版本的方案中,含义reduce是相同的。

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

相当于

(curry (curry 1 2) 3)

“标准”的定义reduce+只有当列表为空时才使用参数。

因此,您需要重新定义reduce以使其行为符合您的预期。

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

在其他版本的方案中,它可以工作,因为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用于添加)。

于 2020-11-12T11:43:30.577 回答
0

在@alinsoar 的帮助下,我在MIT Scheme 文档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)
于 2020-11-15T18:00:44.227 回答