如何在Scheme中定义'和'?

use*_*219 3 scheme racket

Scheme中的'和'将忽略错误'除以0',如(和(负)(随机100))(/ 1 0))返回#f.它是如何做到的?

i(定义(∧b0 b 1)(if(not b 0)#f b 1)),(∧(负?(随机100))(/ 1 0))仍然进入'除以0'错误.

Tik*_*vis 5

您不能and直接定义为函数,因为Scheme是严格的 -这意味着函数参数在传递给函数之前总是被计算.

但是,您可以and使用宏定义适当的短路.这是Racket中最简单的版本:

(define-syntax-rule (my-and a b) (if a b #f))
Run Code Online (Sandbox Code Playgroud)

syntax-rules标准计划中使用的等效表格:

(define-syntax my-and
  (syntax-rules ()
    [(_ a b) (if a b #f)]))
Run Code Online (Sandbox Code Playgroud)

宏不是正常的功能.相反,它是一个在编译时运行的语法转换.当您and在代码中使用new 时,它会"扩展"到相应的if表达式.所以:

(my-and #f (/ 1 0))
Run Code Online (Sandbox Code Playgroud)

变成了

(if #f (/ 1 0) #f)
Run Code Online (Sandbox Code Playgroud)

在程序运行之前.由于if内置于语言中,因此具有正确的行为.

由于宏不是函数,它也意味着你不能作为参数传递.所以你不能and直接使用折叠- 你必须将它包装成lambda.

为了更忠实于原始and,你可以定义my-and通过使宏递归来获取任意数量的参数.要在宏中定义"rest参数",请使用special ...关键字:

(define-syntax my-and
  (syntax-rules ()
    [(_)         #t]
    [(_ a)       a]
    [(_ a b ...) (if a (my-and b ...) #f)]))
Run Code Online (Sandbox Code Playgroud)

如果你使用像Racket #lang lazy或Haskell 这样的惰性语言,那么你不需要在这里使用宏.您可以直接定义and:

#lang lazy
(define (and a b) (if a b #f))
Run Code Online (Sandbox Code Playgroud)

或者在Haskell:

and a b = if a then b else False
Run Code Online (Sandbox Code Playgroud)

并且它将具有正确的行为,作为正常功能.你可以将它传递and给折叠,它甚至会在遇到它时立即停止评估列表False!看一看:

Prelude> foldl and True [True, False, error "fail"]
False
Run Code Online (Sandbox Code Playgroud)

(error在Haskell中出错就像1/0.因为Haskell是静态类型的,所以and必须是booleans 的参数,所以你不能1/0直接使用.)

  • 为了清楚起见,宏是一个编译时生物,它重写你的方案程序AST.可以把它想象成一个来自`AST - > AST`的函数,它在编译时运行 (2认同)