为何Scheme的filter表单可能不按顺序处理列表元素?如何实现顺序filter
按顺序执行的过滤函数filterfunc实现
(filter procedure list)会对list的每个元素应用procedure,返回仅包含所有令procedure返回真值的元素的新列表。
(R. Kent Dybvig 《The Scheme Programming Language》)
R6RS标准仅要求filter返回列表的元素顺序和输入列表一致,并未规定procedure的调用顺序。如果你的过滤逻辑依赖判断过程的调用顺序(比如判断过程带内部状态、需要按顺序产生副作用的场景),原生filter的行为存在不确定性,需要自行实现严格按输入列表顺序调用判断过程的filterfunc。
如果你使用Racket环境,原生的filter已经默认按从前到后的顺序执行判断过程,不需要额外实现。如果需要写跨R6RS实现、严格保证调用顺序的可移植代码,可以参考以下两种实现:
基础递归实现(易读版)
该实现逻辑直观,天然保证先处理列表头部元素再处理后续元素:
(define (filterfunc proc lst) (cond ((null? lst) '()) (else ;; 先对当前头部元素执行判断,保证执行顺序 (let ((check-result (proc (car lst)))) (if check-result (cons (car lst) (filterfunc proc (cdr lst))) (filterfunc proc (cdr lst)))))))
尾递归优化版(适用于长列表)
该实现被Scheme标准要求的尾递归优化机制转换为迭代执行,处理长列表不会出现栈溢出,同样严格保证调用顺序:
(define (filterfunc proc lst) (let loop ((remaining lst) (result-acc '())) (cond ((null? remaining) (reverse result-acc)) (else (loop (cdr remaining) ;; 先执行当前元素的判断再进入下一轮迭代 (if (proc (car remaining)) (cons (car remaining) result-acc) result-acc))))))
调用顺序验证
可以通过带打印副作用的判断过程验证执行顺序符合预期:
;; 带副作用的判断函数:判断是否为偶数,同时打印当前处理的元素 (define (even?/print n) (display "正在处理元素:") (display n) (newline) (even? n)) ;; 调用测试 (filterfunc even?/print '(1 2 3 4 5 6))
执行后会按1、2、3、4、5、6的顺序打印处理的元素,确认调用顺序和输入列表完全一致。
内容的提问来源于stack exchange,提问作者mnemenaut
相关产品推荐
相关产品推荐

