我主要是一个C++(因此是一个OO/命令式)程序员,我觉得很奇怪,在条件语句中,每个评估只能有一个语句,例如函数式语言Scheme中的if语句。
例如:
(let ((arg1 0) (arg2 1))
(if (> arg1 arg2)
arg1
arg2)))
错误的例子:
(let ((arg1 0) (arg2 1))
(if (> arg1 arg2)
(arg1 (display "cool"))
(arg2 (display "not cool"))))
给了我一个类型的错误"程序应用程序:预期程序,给定:2;论据是:#void"
这可以通过将所述条件语句放入已定义函数的主体中的不同语句来解决,例如,条件语句的主体每次都有单独的语句,如下所示:
(if (condition) statement1a statement2a)
(if (condition) statement1b statement2b)
等等...
不用说,它不太实用。更不用说重复的代码开销了。
我在这里错过了什么还是真的没有其他方法?
(let((arg1 0)(arg2 1))
(if (> arg1 arg2)
(begin
(display arg1)
(newline)
(display "cool"))
(begin
(display arg2)
(newline)
(display "not cool"))))
当你说(arg1(disply "cool"))时,你是在暗示arg1应该是一个过程。
你可能遗漏的一件事是,在 Scheme 中没有"语句"这样的东西。一切都是表达式,您可能认为语句的内容也会返回一个值。这适用于 if
,通常用于返回一个值(例如,(if (tea-drinker?) 'tea 'coffee)
.与C++不同,条件的大多数用途不是用于改变变量或打印值。这减少了在 if
子句中包含多个表达式的需要。
但是,正如Ross和Rajesh所指出的,您可以在if
子句中使用cond
(推荐)或使用begin
s。请注意,如果在条件中有许多副作用计算,则可能不会习惯性地使用 Scheme。
@RajeshBhat给出了一个使用 begin with if 语句的很好的例子。
另一种解决方案是cond
形式
(let ([arg1 0] [arg2 1])
(cond
[(< arg1 0) (display "negative!")]
[(> arg1 arg2) (display arg1) (newline) (display "cool")]
[else (display arg2) (newline) (display "not cool")]))
cond
表单中的每一行都有一个隐式begin
,如果您查看cond
的实现,您实际上可以看到该。
(链接是Chez Scheme文档,可能(阅读:可能)与您正在使用的实现不同,因为它是专有的,尽管Petite Chez是免费的(在小型版本中没有编译器))
http://scheme.com/tspl4/syntax.html#./syntax:s39
编辑:关于开始形式的重要说明,因此所有具有隐式开始的表达式。
以下代码
(+ 2 (begin 3 4 5))
计算结果为 7。这是因为begin
窗体的返回值是其最后一个表达式。这只是使用开始时要记住的事情。但是,使用副作用和显示器之类的东西在 3 和 4 所在的位置可以正常工作。
既然您已经在"内部"过程中使用了迭代过程,为什么不使用命名 let 来使用此定义
(define (fact n)
(let inner ((counter 1) (result 1))
(if (> counter n)
result
(inner (+ counter 1) (* result counter)))))
由于进程的状态只能用 2 个变量确定,因此它不会使用那么多内存。
例如(事实 6)是这样计算的
(inner 1 1)
(inner 2 1)
(inner 3 2)
(inner 4 6)
(inner 5 24)
(inner 6 120)
(inner 7 720)
720
这是同一过程的 letrec 版本:
(define (fact n)
(letrec ((inner
(lambda (counter result)
(if (> counter n)
result
(inner (+ counter 1) (* result counter))))))
(inner 1 1)))
如果您觉得受到 Scheme 语法的限制,您可以随时通过定义宏来更改语法。宏类似于 lambda,只不过它在编译时生成代码(如C++模板),并且在调用宏之前不会计算其参数。
您可以轻松地制作一个宏,让您使用通常表示过程应用程序的语法,例如 (arg1 "cool")
,表示"在每个项目后用换行符显示括号内的所有内容"。(当然,这意味着仅在宏内部。喜欢这个:
(define-syntax print-if
(syntax-rules ()
[(_ c (true-print ...) (false-print ...))
(if c
(display-with-newlines true-print ...)
(display-with-newlines false-print ...))]))
(define-syntax display-with-newlines
(syntax-rules ()
[(_ e)
(begin (display e) (newline))]
[(_ e0 e* ...)
(begin (display-with-newlines e0) (display-with-newlines e* ...)) ]))
(let ([arg1 0] [arg2 1])
(print-if (> arg1 arg2)
(arg1 "cool")
(arg2 "not cool")))
输出:
1
not cool
如果您现在不了解宏定义的工作原理,请不要担心。如果你只是在掌握C++后尝试Scheme,毫无疑问你正在经历很多挫折。你应该对Scheme真正拥有的力量和灵活性有所了解。
方案宏和C++模板之间的一个很大区别是,在宏中,您可以使用整个方案语言。宏使用 Scheme 告诉如何以您喜欢的任何完全任意的方式将 s-expr 转换为 Scheme 代码。然后,编译器编译宏输出的方案代码。由于 Scheme 程序本身就是 s-exprs,因此基本上没有任何限制(除了词法范围和需要将所有内容括在括号中)。
如果你愿意,不要让任何人阻止你使用副作用。Scheme的荣耀在于你可以做任何你想做的事。