5

基本上,我想使用 map 在列表中进行选择,例如

(define tbl '(a b c d))
(map (lambda (item 'c) (if (eq? item 'c) item (...what in else?) )))

我想要的结果是

'(c)

我尝试将 else 部分留空,它抱怨需要 else 部分。我试过了

(display "foo") 

作为其他部分并得到

(#<void> #<void> c #<void>)

那很接近。

有什么方法可以使用地图来获取'(c)?我知道递归方式,但我想知道 map 是否也可以这样做。如果不是 '(c),至少 (##c#) 但不使用 display hack 来实现 void 类型的返回值。


4

3 回答 3

12

您想使用filter,而不是map- 因为输出列表的元素可能少于输入列表。所有这些#<void>返回的值display都在那里,因为map将始终在输出列表中包含一个结果,即使对于那些我们不感兴趣的元素也是如此。

(define tbl '(a b c d))

(filter (lambda (item) (eq? item 'c)) tbl)
=> '(c)

等效地,并且有点短:

(filter (curry eq? 'c) tbl)
=> '(c)

map当您想要对输入列表中的每个元素执行某些操作而不丢弃元素时使用。另一方面,filter用于选择输入列表中的一些元素,那些对#t给定谓词求值的元素,并且filter在大多数 Scheme 解释器中都可用,如果它不可用,您可以导入SRFI-1或使用参考实现

没有办法'(c)使用 using (可以使用plus或等破解它,但这不是想法,是吗?);如果由于某种原因您必须只使用并且不介意返回带有占位符的列表,这里有几个选择: mapmapapplyremove*map

(map (lambda (item) (if (eq? item 'c) item '%)) tbl) ; placeholder in else part
=> '(% % c %)

(map (lambda (item) (when (eq? item 'c) item)) tbl)  ; when has implicit #<void>
=> '(#<void> #<void> c #<void>)

是时候进行一些黑客攻击了。使用map加号apply(如@WillNess 的回答中所述),这具有在任何 RxRS 解释器中工作的优势,并且是最便携的解决方案,因为它使用标准程序:

(apply append (map (lambda (item) (if (eq? item 'c) (list item) '())) tbl))
=> '(c)

使用map加号remove*

(remove* (list (void)) (map (lambda (item) (when (eq? item 'c) item)) tbl))
=> '(c)

对于更改,没有map- 使用的解决方案foldr

(foldr (lambda (item a) (append (if (eq? item 'c) (list item) '()) a)) '() tbl)
=> '(c)

当然,你总是可以filter使用标准过程实现你自己的版本,这也可以在所有 RxRS 解释器之间移植:

(define (filter pred? lst)
  (cond ((null? lst)
         '())
        ((not (pred? (car lst)))
         (filter pred? (cdr lst)))
        (else
         (cons (car lst)
               (filter pred? (cdr lst))))))

(filter (lambda (item) (eq? item 'c)) tbl)
=> '(c)
于 2013-07-16T15:31:13.783 回答
3

简单的“标准”技巧是

(apply append (map (lambda(x)(if (eq? x 'c) (list x) '())) '(a b c d)))
;Value 12: (c)

返回(c)。该apply append ... map组合在 Common Lisp 中被称为mapcan(“mapcan”表示“map and concatenate”):

[1]> (mapcan #'(lambda(x)(if (eq x 'c) (list x))) '(a b c d))
(C)

MIT Scheme 也有这个功能。

apply append展平其参数列表的一级(apply append '((a) () (c) ())) == (append '(a) '() '(c) '()) --> (a c)。由于空列表消失了,它对于消除带有map. 这与您有一个可用的效果相同filter(它不在R5RS中,但在SRFI1中)。

它也可以用于其他效果,例如加倍:

[2]> (mapcan #'(lambda(x)(if (evenp x) (list x x))) '(1 2 3 4))
(2 2 4 4)

顺便说一句,mapcan就是所谓的 list monad 的“绑定”(参数翻转),以及(apply append ...)它的“加入”操作。确实,对于任何 monad 都必须如此,对于列表也是如此 bind m f == join (map f m)

这也是 Haskell列表推导的基础:

Prelude> [y | x<-[1,2,3,4], even x, y<-[x,x]]
[2,2,4,4]
于 2013-07-19T08:47:34.527 回答
1

你没有提到你的方案版本/环境。假设你只有最基本的 Scheme,实现一些东西很容易:

(define (choose-if pred list)
  (let choosing ((list list) (rslt '()))
    (if (null? list)
        (reverse rslt)
        (choosing (cdr list)
                  (if (pred (car list))
                      (cons (car list) rslt)
                      rslt)))))

然后相关:

(define (choose item list)
  (choose-if (lambda (elt) (eq? item elt)) list))

(define (choose-if-not pred list)
  (choose-if (lambda (elt) (not (pred elt))) list))

并使用:

> (choose 'c '(a b c d))
(c)

您还可以选择使用低级原语,例如:

(define (choose item list)
  (remq #f (map (lambda (elt) (eq? item elt)) list)))

或者

(define (choose item list)
   (remp (lambda (elt) (not (eq? item elt))) list))
于 2013-07-16T20:43:52.953 回答