“普通”函数通常仅在给定类型的对象域上定义,但某些函数(如Scheme类型谓词list?
或procedure?
)是为任何类型的参数定义的,甚至可以适用于自己。所以例如(list? procedure?)
评估为#f
,(procedure? procedure?)
评估为#t
。我试图找出如何编写这种完全定义的函数,但是却找不到讨论这个的源。
例如,假设我们使用以下构造函数和选择器实现了Structure and Interpretation of Computer Programs的练习2.4中描述的对数据类型:
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
我如何定义谓词pair?
,该谓词#t
为使用cons
构建的任何内容返回#f
,而list?
用于任何不是?更一般地说,如何实现procedure?
和{{1}}等类型谓词?
答案 0 :(得分:4)
这很容易。重新定义过程以使第一个参数成为您要创建的对象类型:
(define +type-pair+ 'pair)
(define +type-number+ 'number)
(define (cons a d)
(lambda (m) (m +type-pair+ a d)))
(define (type x)
(x (lambda (t . rest) t)))
(define (pair? c)
(eq? (type c) +type-pair+))
(define (car c)
(if (pair? c)
(c (lambda (t a d) a))
(error "Argument is not pair!")))
(define (cdr c)
(if (pair? c)
(c (lambda (t a d) d))
(error "Argument is not pair!")))
(define (number? c)
(if (type c) +type-number+))
您可以使用它来定义您的语言,甚至程序中的所有内容,但所有人都必须支持使用(类型x)给出类型,并且所有使用某些内容的过程必须确保类型正确。将对实现为闭包可能是最慢的方式,因此只有SICP思想实验才能让您了解闭包是什么。 Arc使用所有数据类型的标记,但将数据保存为数据。你应该看看Arc,看看标签是如何成为一种可行的方法。
答案 1 :(得分:3)
顺便说一下,在Racket中,使用struct进行的结构谓词已经为你工作了,所以你不必手动实现它。
每个专业级方案都通过结构或记录提供类似的功能。请参阅SRFI-9,其中描述了许多其他Scheme实现将为您做的事情。
答案 2 :(得分:1)
你不能,你已经显示的代码。程序是不透明的。
作为一种可能性,您必须更改构造函数/访问者以使用标记的列表。
(define *church-pair-tag* (list '*church-pair-tag*))
(define (cons x y)
(cons *church-pair-tag* (lambda(m) (m x y))))
(define (church-pair? x)
(and (pair? x)
(eq? *church-pair-tag* (car x))
(procedure? (cdr x))))
....
但这显然不是万无一失的。
谓词pair?
和procedure?
是内置的,可能是通过某种内幕魔术来实现的。 list?
可以pair?
,null?,car
和cdr
来实现。