仅在定义两次时,过程才有效

时间:2018-01-23 18:49:40

标签: scheme r5rs

我已经定义了map程序和square程序。 square过程工作正常,但map只能在定义两次时才有效。

给出以下代码:

; Squares a number.
(define square (lambda (n)
    (* n n)
))


; Applies function f on all elements of l.
(define map (lambda (l f)
    (cond
        ((null? l) '())
        (else (cons (f (car l)) (map (cdr l) f)))
    )
))

此程序在执行时崩溃:

> (map '(1 2 3) square)
; mcar: contract violation
;   expected: mpair?
;   given: #<procedure:square>
; [,bt for context]

但是,鉴于以下代码,程序按预期工作。唯一的区别是map现在定义了两次。

; Squares a number.
(define square (lambda (n)
    (* n n)
))


; Applies function f on all elements of l.
(define map (lambda (l f)
    (cond
        ((null? l) '())
        (else (cons (f (car l)) (map (cdr l) f)))
    )
))

(define map (lambda (l f)
    (cond
        ((null? l) '())
        (else (cons (f (car l)) (map (cdr l) f)))
    )
))

此版本可以正常使用:

> (map '(1 2 3) square)
{1 4 9}

是什么导致了这个问题,应该如何解决?

2 个答案:

答案 0 :(得分:4)

我无法重现您的问题。具体来说,我在DrRacket中运行这个程序:

#lang r5rs

; Squares a number.
(define square (lambda (n)
    (* n n)
))


; Applies function f on all elements of l.
(define map (lambda (l f)
    (cond
        ((null? l) '())
        (else (cons (f (car l)) (map (cdr l) f)))
    )
))

然后,在交互窗口中,我运行

> (map '(3 4 5) square)

...得到结果:

(mcons 9 (mcons 16 (mcons 25 '())))

您能提供更多信息以帮助重现您的问题吗? (你提到的mcons清楚地表明你是使用命令行运行这个代码,但是我猜你正在加载和运行你的代码的方式会有一些有趣的事情发生。)

编辑:好的,我现在能够通过将这些表达式逐个粘贴到REPL中来重现这样的事情。自从我使用顶级REPL以来,显然已经很久了。无论您的问题的答案如何,更高级别的答案是:不要将表达式粘贴到REPL中。用Matthew Flatt(Racket的主要实现者)的话说,“最高级别是没有希望的。”使用DrRacket是解决此问题的最简单方法。

编辑2:我怀疑,TL; DR:1)顶级是没有希望的。 2)将所有代码放在模块中。

我在向Racket-Users列表发帖中总结了一些混乱。具体来说,基本问题是:绑定的右侧怎么不在绑定本身的范围内?

以下是马修回答的摘录:

这就是问题的本质。哪些事情属于一个范围 顶级定义?

例如,在f的绑定范围内引用f

(define (g x) (f x))
(define (f x) x)

中怎么样
(begin
  (define (g x) (f x))
  (define (f x) x))

(expand '(define (f x) x))
(define (g x) (f x))

(begin
 (expand '(define (f x) x))
 (define (g x) (f x)))

Racket中的规则是顶级define不会改变 绑定标识符直到评估define。所以,在

(define (map x) ... map ...)

map的引用在map点扩展/编译 是指模块导入,而不是名为map的变量。到时候了 评估定义,重定向map的含义为时已晚 作为导入的参考。

还有其他选择,但我认为没有其他选择 最终明显更好或更一致。顶级是 无望。

模块表现得更好,部分原因是因为a的范围 定义很明确:从模块的开始到结束。

此时,你可能会问你 应该如何与Racket互动。有几个不错的选择:

1)使用DrRacket。我不能高度推荐这个。 2)使用命令行和“require”而不是“load”。 “加载”,而不是在它上面写得太精细,是旧版本语言的半破坏保留。

因此,例如,我可以将此代码放在名为'a.rkt'的文件中:

#lang r5rs

; Squares a number.
(define square (lambda (n)
    (* n n)
))


; Applies function f on all elements of l.
(define map (lambda (l f)
    (cond
        ((null? l) '())
        (else (cons (f (car l)) (map (cdr l) f)))
    )
))

然后,我启动球拍并“需要”此模块,然后使用',输入'进入模块:

hardy:/tmp clements> racket
Welcome to Racket v6.11.0.6.
> (require "a.rkt")
> ,enter "a.rkt"
"a.rkt"> (map '(3 4 5) square)
(mcons 9 (mcons 16 (mcons 25 '())))
"a.rkt"> 

让我再说一遍,你只需使用DrRacket即可回避所有这些问题。

所以......我今天学会了一堆!谢谢!

答案 1 :(得分:1)

map是标准的Scheme函数。当你第一次定义这个函数时,它显然试图调用标准函数,而不是重新定义它。由于标准函数以相反的顺序(map function list)获取其参数,因此会出错。第二次定义它时,它会在环境中找到你的功能,所以工作正常。

最佳解决方案是使用与标准功能不冲突的其他名称。