5

“普通”函数通常只在给定类型的对象域上定义,但某些函数,例如 Scheme 类型谓词list?procedure?,是为任何类型的参数定义的,甚至可以应用于它们自己。所以 eg(list? procedure?)评估为#f(procedure? procedure?)评估为#t。我试图弄清楚如何编写此类完全定义的函数,但无法找到讨论此问题的来源。

例如,假设我们使用以下构造函数和选择器实现了计算机程序的结构和解释的练习 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?实现的?

4

3 回答 3

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+))

您可以使用它来定义您的语言中的所有内容,甚至是程序,但所有程序都必须支持使用 (type x) 为其指定类型,并且所有使用某些内容的程序必须确保类型正确。将对实现为闭包可能是最慢的方法,因此这只是 SICP 思想实验,让您了解闭包是什么。Arc 对所有数据类型都使用标签,但将数据保留为数据。您应该看看 Arc 以了解标记如何成为一种方法。

于 2012-08-06T12:51:29.820 回答
3

顺便说一句,在 Racket 中,用struct制作的结构谓词已经为您完成了这项工作,因此您不必手动实现它。

每个专业级别的方案都通过结构或记录提供类似的功能。请参阅SRFI-9,其中描述了许多其他 Scheme 实现已经为您做的事情。

于 2012-08-06T22:58:06.773 回答
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

于 2012-08-05T22:50:34.300 回答