例如:如果我希望函数equal?
识别我自己的类型或记录,我可以添加equal?
的新行为吗?没有删除或覆盖旧的?
或者例如,如果我想使函数"+"
也接受字符串?
答案 0 :(得分:3)
而不是使用import
,更好的解决方案是通过let
跟踪原始函数 - 绑定它。最好检查参数的类型是否为字符串,而不是它不是数字。使用这两种方法意味着可以组合技术。
(define +
(let ((old+ +))
(lambda args
(if (string? (car args))
(apply string-append args)
(apply old+ args)))))
(define +
(let ((old+ +))
(lambda args
(if (vector? (car args))
(apply vector-append args)
(apply old+ args)))))
以上将生成一个+
函数,可以处理数字,字符串或向量。总的来说,这是一种更具扩展性的方法。
我能够验证上述在MIT / GNU Scheme,Guile,Racket,Chicken,TinyScheme和SCSH中是否正常工作。但是,在某些实现中,例如Biwa Scheme,有必要使用set!
而不是define
。在Ikarus中,set!
不能用于导入的基元,define
会扰乱环境,因此有必要分两步完成:
(define new+
(let ((old+ +))
(lambda args
(if (string? (car args))
(apply string-append args)
(apply old+ args)))))
(define + new+)
请注意,根据R5RS,define
和set!
在这种情况下应该是等效的:
在程序的顶层,定义
(define <variable> <expression>)
与赋值表达式具有基本相同的效果
(set! <variable> <expression>)
如果绑定
<variable>
。
答案 1 :(得分:1)
到目前为止,解决方案在R6RS / R7RS环境中的工作效果不尽如人意。当我开始玩这个时,我正在思考泛型,但我并不想推出自己的类型系统。相反,您提供了一个谓词过程,该过程应确保参数适用于此特定过程。它并不完美,但它与其他R5RS的答案类似,你永远不会重新定义程序。
我已经在R6RS中写了所有内容,但我想它很容易移植到R7RS。 这是一个例子:
#!r6rs
(import (sylwester generic)
(rename (rnrs) (+ rnrs:+))
(only (srfi :43) vector-append))
(define-generic + rnrs:+)
(add-method + (lambda x (string? (car x))) string-append)
(add-method + (lambda x (vector? (car x))) vector-append)
(+ 4 5) ; ==> 9
(+ "Hello," " world!") ; ==> "Hello, world!"
(+ '#(1) '#(2)) ; ==> #(1 2)
正如您所看到的,我使用其他名称导入+
,因此我不需要重新定义它(这是不允许的)。
这是图书馆的实施:
#!r6rs
(library (sylwester generic)
(export define-generic add-method)
(import (rnrs))
(define add-method-tag (make-vector 1))
(define-syntax define-generic
(syntax-rules ()
((_ name default-procedure)
(define name
(let ((procs (list (cons (lambda x #t) default-procedure))))
(define (add-proc id pred proc)
(set! procs (cons (cons pred proc) procs)))
(add-proc #t
(lambda x (eq? (car x) add-method-tag))
add-proc)
(lambda x
(let loop ((procs procs))
(if (apply (caar procs) x)
(apply (cdar procs) x)
(loop (cdr procs))))))))))
(define (add-method name pred proc)
(name add-method-tag pred proc)))
正如您所看到的,我使用消息传递来添加更多方法。
答案 2 :(得分:0)
诀窍是定义自己的扩展函数,使其隐藏标准函数,但在需要时调用标准函数。在扩展功能中,您可以执行import
来获得标准功能。这是+
的一个版本,它也接受字符串:
(define +
(lambda args
(if (number? (car args))
(let ()
(import (scheme))
(apply + args))
(apply string-append args))))
(这有点草率,因为它假设至少有一个参数,它只检查第一个参数的类型。但它说明了这种技术。)
答案 3 :(得分:0)
不是纯粹的Scheme,但在Guile例如你可以使用CLOS - 就像OO系统一样:
scheme@(guile-user)> (use-modules (oop goops))
scheme@(guile-user)> (define-method (+ (x <string>) ...) (string-append x ...))
scheme@(guile-user)> (+ "a" "b")
$1 = "ab"
scheme@(guile-user)> (+ "a" "b" "c")
$2 = "abc"
scheme@(guile-user)> (+ 1 2)
$3 = 3
scheme@(guile-user)> (+ 1 2 3)
$4 = 6
答案 4 :(得分:0)
您无法使用
(define +
(let ((old+ +))
...))
因为define
为其init表单设置了递归环境。因此,在评估+
中的(old+ +)
时,它将不受约束。就这样:
> (define +
(let ((old+ +))
(lambda (a b) (display "my+") (old+ a b))))
Unhandled exception
Condition components:
1. &undefined
2. &who: eval
3. &message: "unbound variable"
4. &irritants: (+)
以下作品:
> (define old+ +)
> (define + (lambda (a b) (display "my+\n") (old+ a b)))
> (+ 1 2)
my+
3
虽然不是那么漂亮。
答案 5 :(得分:0)
在R7RS大型版本(或实际上在任何Scheme中)中,可以使用SRFI 128比较器,该打包器将相等,排序和哈希的思想打包在一起,以使通用比较成为可能。 SRFI 128允许您创建自己的比较器并将其用于支持比较器的功能。例如,在比较器的排序谓词意义上,<?
接受一个比较器对象和两个或多个与该比较器关联的类型的对象,如果第一个对象小于第二个对象,则返回#t