Racket 的 findf 和 for/first 函数有什么区别?
What's the difference between Racket's findf and for/first functions?
Racket 有一个 findf
函数,可以让你找到列表中的第一个匹配元素...
(findf even? '(1 2 3 4))
然而,它也有一个 for/first
函数,它似乎做同样的事情,尽管语法更复杂......
(for/first ([n '(1 2 3 4)] #:when (even? n)) n)
两者有什么区别,既然findf
看起来更短,我为什么要用for/first
?
谢谢
不同之处在于 for/first
像 for
一样迭代,因此您可以充分利用 Racket 的 for
语法:
> (for/first ([i '(1 2 3)]
[j "abc"]
#:when (odd? i)
[k #(#t #f)])
(list i j k))
'(1 #\a #t)
要使用 findf
实现相同的效果,您必须预先生成整个列表:
> (findf (λ (x) (odd? (first x)))
(for/list ([i '(1 2 3)]
[j "abc"]
[k #(#t #f)])
(list i j k)))
'(1 #\a #t)
对于搜索单个列表,您是正确的,使用 findf
可能是正确的功能。它很简单,可以满足您的需求。但是,如果您想搜索更复杂的列表或需要创建内联列表,for/first
更好。
这里有一些更简单的例子来向您展示 for/first
(因此 for
)的力量。
假设您有两个变量,一个数字列表和一个字母表字符串,您希望将它们一对一配对,return 第一对数字为偶数。
(define numbers (range 1 27)) ; numbers 1 to 26
(define alphabet "abcdefghijklmnopqrstuvwxyz")
使用findf
,你需要先将字符串转换成一个字符列表,然后将两个列表压缩成对,然后创建一个lambda(匿名函数)来检查给定的对是否有一个偶数。
(findf (lambda (pair) (even? (first pair)))
(map list numbers (string->list alphabet)))
使用for/first
,需要为每个序列分配一个标识符,然后在#:when
子句中引用数字标识符来检查数字是否为偶数。 (for
并行遍历每个序列,在字符串上为您调用 string->list
,并且仅在 #:when
return 为真时才计算正文。)
(for/first ([num numbers]
[str alphabet]
#:when (even? num))
(list num str))
就字符数而言,它们大致相等(94 到 102),但就清晰度而言,我认为 for/first
的作用更明显:获取两个集合,遍历两者同时进行,仅在数字为偶数时评估主体,并且 return 列表。
如果我们想要 return 第一对数字大于 10 的偶数怎么办?在这里我们开始看到事情变得笨拙。
(findf (lambda (pair)
(let ([num (first pair)])
(and (even? num)
(> num 10))))
(map list numbers (string->list alphabet)))
对比
(for/first ([num numbers]
[str alphabet]
#:when (even? num)
#:when (> num 10))
(list num str))
;; or
(for/first ([num numbers]
[str alphabet]
#:when (and (even? num)
(> num 10)))
(list num str))
每个 for
变体(for/first
、for/list
等)对主体的处理略有不同,但迭代逻辑是相同的,允许作者完全符合他们的意图,而无需重新实现该逻辑。
Racket 有一个 findf
函数,可以让你找到列表中的第一个匹配元素...
(findf even? '(1 2 3 4))
然而,它也有一个 for/first
函数,它似乎做同样的事情,尽管语法更复杂......
(for/first ([n '(1 2 3 4)] #:when (even? n)) n)
两者有什么区别,既然findf
看起来更短,我为什么要用for/first
?
谢谢
不同之处在于 for/first
像 for
一样迭代,因此您可以充分利用 Racket 的 for
语法:
> (for/first ([i '(1 2 3)]
[j "abc"]
#:when (odd? i)
[k #(#t #f)])
(list i j k))
'(1 #\a #t)
要使用 findf
实现相同的效果,您必须预先生成整个列表:
> (findf (λ (x) (odd? (first x)))
(for/list ([i '(1 2 3)]
[j "abc"]
[k #(#t #f)])
(list i j k)))
'(1 #\a #t)
对于搜索单个列表,您是正确的,使用 findf
可能是正确的功能。它很简单,可以满足您的需求。但是,如果您想搜索更复杂的列表或需要创建内联列表,for/first
更好。
这里有一些更简单的例子来向您展示 for/first
(因此 for
)的力量。
假设您有两个变量,一个数字列表和一个字母表字符串,您希望将它们一对一配对,return 第一对数字为偶数。
(define numbers (range 1 27)) ; numbers 1 to 26
(define alphabet "abcdefghijklmnopqrstuvwxyz")
使用findf
,你需要先将字符串转换成一个字符列表,然后将两个列表压缩成对,然后创建一个lambda(匿名函数)来检查给定的对是否有一个偶数。
(findf (lambda (pair) (even? (first pair)))
(map list numbers (string->list alphabet)))
使用for/first
,需要为每个序列分配一个标识符,然后在#:when
子句中引用数字标识符来检查数字是否为偶数。 (for
并行遍历每个序列,在字符串上为您调用 string->list
,并且仅在 #:when
return 为真时才计算正文。)
(for/first ([num numbers]
[str alphabet]
#:when (even? num))
(list num str))
就字符数而言,它们大致相等(94 到 102),但就清晰度而言,我认为 for/first
的作用更明显:获取两个集合,遍历两者同时进行,仅在数字为偶数时评估主体,并且 return 列表。
如果我们想要 return 第一对数字大于 10 的偶数怎么办?在这里我们开始看到事情变得笨拙。
(findf (lambda (pair)
(let ([num (first pair)])
(and (even? num)
(> num 10))))
(map list numbers (string->list alphabet)))
对比
(for/first ([num numbers]
[str alphabet]
#:when (even? num)
#:when (> num 10))
(list num str))
;; or
(for/first ([num numbers]
[str alphabet]
#:when (and (even? num)
(> num 10)))
(list num str))
每个 for
变体(for/first
、for/list
等)对主体的处理略有不同,但迭代逻辑是相同的,允许作者完全符合他们的意图,而无需重新实现该逻辑。