Inheritance 有没有可能;扩展;方案中的函数/lambda/宏?
例如:如果我想让函数Inheritance 有没有可能;扩展;方案中的函数/lambda/宏?,inheritance,scheme,r5rs,r6rs,r7rs,Inheritance,Scheme,R5rs,R6rs,R7rs,例如:如果我想让函数equal?识别我自己的类型或记录,我可以添加equal?的新行为吗?不删除或覆盖旧的 或者,如果我想让函数“+”也接受字符串?诀窍是定义您自己的扩展函数,使其隐藏标准函数,但在需要时调用标准函数。在扩展函数中,您可以执行导入以获取标准函数。下面是一个版本的+,它也接受字符串: (define + (lambda args (if (number? (car args)) (let () (import (scheme))
equal?
识别我自己的类型或记录,我可以添加equal?
的新行为吗?不删除或覆盖旧的
或者,如果我想让函数
“+”
也接受字符串?诀窍是定义您自己的扩展函数,使其隐藏标准函数,但在需要时调用标准函数。在扩展函数中,您可以执行导入
以获取标准函数。下面是一个版本的+
,它也接受字符串:
(define +
(lambda args
(if (number? (car args))
(let ()
(import (scheme))
(apply + args))
(apply string-append args))))
(这有点草率,因为它假设至少有一个参数,并且只检查第一个参数的类型。但它说明了这项技术。)与其使用
导入
,不如通过让它绑定来跟踪原始函数。最好检查参数的类型是字符串,而不是数字。使用这两种方法意味着可以组合该技术
(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)))))
上述操作将生成一个处理数字、字符串或向量的+
函数。一般来说,这是一种更具扩展性的方法
我能够在麻省理工学院/GNU方案、诡计、球拍、鸡肉、TinyScheme和SCSH中验证上述方法是否正确。然而,在一些实现中,例如Biwa方案,有必要使用set代码>而不是定义
。在Ikarus中,set代码>不能用于导入的原语,并且定义
会弄乱环境,因此有必要分两步执行此操作:
(define new+
(let ((old+ +))
(lambda args
(if (string? (car args))
(apply string-append args)
(apply old+ args)))))
(define + new+)
请注意,根据,定义
和设置在这种情况下,代码>应该是等效的:
在程序的顶层,定义
(define <variable> <expression>)
(定义)
本质上与赋值表达式具有相同的效果
(set! <variable> <expression>)
(设置!)
如果
已绑定
不是纯方案,但在示例中,您可以使用类似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
scheme@(guile user)>(使用模块(oop-goops))
scheme@(guile user)>(定义方法(+(x)…)(字符串附加x…)
方案@(欺诈用户)>(+“a”“b”)
$1=“ab”
方案@(欺诈用户)>(+“a”“b”“c”)
$2=“abc”
方案@(欺诈用户)>(+12)
$3 = 3
方案@(欺诈用户)>(+1 2 3)
$4 = 6
您不能使用
(define +
(let ((old+ +))
...))
因为define
为其init表单设置了一个递归环境。因此,当在(旧++)
中计算++
时,它将被解除绑定。因此:
> (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
虽然它不是那么漂亮。到目前为止,解决方案在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)))
正如您所看到的,我使用消息传递来添加更多的方法。在R7RS large(或者在任何方案中,实际上)中,您可以使用SRFI 128比较器,它将相等、排序和散列的思想打包在一起,以使通用比较成为可能。SRFI 128允许您创建自己的比较器,并在比较器感知功能中使用它们。例如,如果您想使用相同的方法将函数扩展到另一种类型,则此操作将失败。这一点很好。你的答案好多了。我把我的留给大家,只是为了让大家知道如何不去做。事实上,这对扩展lambda
或宏有好处,因为它们不能绑定在let
中。通过扩展lambda
,我不是指扩展lambda
,而是通过扩展lambda
的标准定义,重新定义lambda
本身的一般含义。最好使用一种技术,允许链接到以前定义的lambda
的任何内容。有没有办法做到这一点?唯一的办法是分两步。例如,要重新定义lambda
以在每次调用使用它定义的函数时显示字符串“hello”:(定义语法旧lambda lambda)
,然后(定义语法lambda(语法规则)((((args…)body…)旧lambda(args…(显示“hello”)body…)(((args body…)旧lambda args(显示)body…)“hello”)body…)
Edit:实际上,这一个不会链接。问题是宏是“惰性地”扩展的,所以如果你尝试链接它,它就会进入无限循环。我问了一个关于这个问题的问题,因为讨论对于commnet来说有点太复杂了(而且我也不知道答案)。old+不是未定义的吗?@Sylwester再次查看-它被绑定在let
中。Racket上有一个注释。默认情况下,所有方法都不起作用。例如#!Racket;==>模块:标识符在+
和#r6rs;=>模块:标识符已导入到+
中。它在旧语言R5RS的Racket中工作(不是#!r5rs模块语言)具有特殊设置“不允许重新定义初始绑定"未选中。在Ikarus中,如果使用--r6rs脚本运行,您将得到异常标识符的多个定义。REPL可能更宽容,但如果您将其用于项目,这将不会有帮助。@Sylvester我不太理解您的建议。此解决方案适用于r6rs,也适用于r7rs?@FelipeMicaroniLalli我测试了f或者在ikarus和racket上都使用R6RS,但这不起作用。我确实设法在r5rs legac上使用了它