【问题标题】:Not return anything from LISP/Scheme不从 LISP/Scheme 返回任何东西
【发布时间】:2013-07-14 21:14:43
【问题描述】:

基本上,我想使用地图在列表中进行选择

(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>)

这很接近。

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


【问题讨论】:

  • 顺便说一句:lambda 作为参数传递给map 必须接收一个 single 参数,你传递了两个,这将不起作用:@987654327 @
  • 你用的是什么解释器?我的猜测是球拍,但请确认
  • @Óscar López,是的,是 Racket,但我希望解决方案可以通用
  • 使用filterlambda,我知道的任何解释器都可以使用。
  • @ÓscarLópez MIT Scheme 说:;Unbound variable: filter.

标签: functional-programming scheme lisp racket map-function


【解决方案1】:

您想使用filter,而不是map - 因为输出列表的元素可能少于输入列表。 display 返回的所有 #&lt;void&gt; 值都在那里,因为 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 或使用reference implementation

没有办法获得'(c) 使用 map (它可以使用map 加上applyremove* 等被黑客攻击,但那不是想法,是它?);如果出于某种原因您必须仅使用 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)

【讨论】:

  • 推荐阅读以更好地理解mapfilter过程:Sequences as Conventional Interfaces in SICP
  • 如果您的实现缺少 curry 函数但支持 SRFI-26,则另一种选择是 (filter (cut eq? 'c ) tbl)。对于任何想要深入挖掘的人来说,map 和 filter 都可以使用 fold 来实现。
  • #&lt;void&gt;nil 的另一个分片,在 Scheme 中被破坏了。什么集群放克。 “不知何故我更喜欢(CDR(ASSQ KEY A-LIST))”——Ashwin Ram
  • 对于更一般的答案:过滤器是SRFI-1 中的标准程序,大多数实现都支持它,而不仅仅是 Racket。那些没有过滤的实施可以加载它reference implementation
  • 我认为我们对 SO 有多个答案,以便每个答案都可以展示其对给定问题的独特看法。我知道您想为 OP 提供最好的包罗万象的答案,但我认为我们必须在 SO 上提供多合一的答案...您的答案是: “使用过滤器代替地图,或使用地图生成占位符”。我的回答显示了一个带有 map 和 append 的解决方案。 GoZoner 展示了删除原语的用法。恕我直言,每个答案都应该独立存在。现在很混乱。现在,这里的其他答案有什么意义?现在它们变得有点多余了...... :) 只是我的 2cts
【解决方案2】:

您没有提及您的方案版本/环境。假设你只有最基本的 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))

【讨论】:

    【解决方案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”表示“映射和连接”):

    [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]
    

    【讨论】:

    • mapcan 的球拍等价物是 append-map
    猜你喜欢
    • 2011-08-06
    • 1970-01-01
    • 2023-03-28
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多