【问题标题】:Scheme fold map and filter functions方案折叠图和过滤功能
【发布时间】:2013-02-03 01:37:40
【问题描述】:

我正在学习如何在方案中使用高阶函数。我得到了使用高阶函数的想法,但是我在使用它们时遇到了麻烦。对于这个学习练习,我宁愿只使用过滤器、折叠和/或映射的某种组合。

例如,我想构造两个列表之间的集合差异,分别称为 A 和 B。我将集合差异定义为 x,这样 x 是 A 的元素,但 x 不是 B 的元素。我只想使用函数映射、过滤和折叠。例如:

令 A = (1 8 6 2)

令 B = (5 7 9 1 6)

A 和 B 的集合差为 (8 2)

这个想法是通过遍历 A 的元素并查看 A 的元素是否等于 B 的元素来构造一个新列表,如果是,则不要将 a 添加到新列表中;否则将 a 添加到新列表中。

我的算法思路是这样的:

设 neq 为“不等于”

  1. 对于 A 中的每个 a 和 B 中的 b 计算表达式:(neq? a b)

    对于 a = 1,我们有:

    (和 (neq?1 5) (neq?1 7) (neq?1 9) (neq?1 1) (neq?1 6))

  2. 如果此表达式为真,则 a 进入新列表;否则不要将 a 添加到新列表中。在我们的示例中,(neq? 1 1) 的计算结果为 false,因此我们不会将 1 添加到新列表中。

我的整个过程几乎都依赖于 1,这就是我遇到麻烦的地方。

  1. 如何执行第 1 步?
  2. 我发现在第 1 步中我需要 mapfold 函数的某种组合,但是如何分发 and a neq b

编辑这是我拥有的最接近的样本:

(fold-right (trace-lambda buggy (a b c) (and (neq? a b))) #t A B)
|(buggy 3 5 #t)
|#t
|(buggy 2 4 #t)
|#t
|(buggy 1 1 #t)
|#f
#f

上面显示了我的匿名函数尝试执行 (and (neq? a b)) 链的痕迹。但是,它只对 A 和 B 中相同位置/索引的元素执行此操作。

非常感谢所有帮助!

【问题讨论】:

  • fold 将始终在 Scheme 中评估列表中的所有应用程序,并且不可能出现短路。 ormap is a much better fit 在这里。 fold-right 的另一个缺点是不是尾递归(与 fold-left 不同)。 :)
  • @WillNess:请参阅下面的答案,即可能发生短路。你只需要打电话/抄送。事实上,没有 call/cc 的严格函数式编程语言似乎漏掉了一些东西。
  • @Marc 可以。 :) 啊,非本地出口。当然,它改变了一切。不一定只是通过 call/cc BTW,在 Common Lisp 中有return-from,或者只是go

标签: list filter functional-programming scheme predicate


【解决方案1】:

member 的简化版本使用fold 很容易实现,当然:

(define (member x lst)
  (fold (lambda (e r)
          (or r (equal? e x)))
        #f lst))

现在,剩下的就很简单了:

(define (difference a b)
  (filter (lambda (x)
            (not (member x b)))
          a))

如果您想将所有这些合并到一个函数中(使用您的 neq?),您可以这样做:

(define (difference a b)
  (filter (lambda (x)
            (fold (lambda (e r)
                    (and r (neq? e x)))
                  #t b))
          a))

【讨论】:

  • 不过,member 的这个定义不会在第一场比赛中短路。
  • @WillNess 它也不返回被匹配的项目,只返回真或假。但它很好地满足了 OP 的目的。
  • @WillNess 另外(当你在这里时),默认的头像生成系统不是 Gravatar 的错;所以选择使用身份图标作为默认头像。如果您需要责备某些事情,请归咎于身份识别算法。
  • 您好,感谢您的回复。对于你的第一个,你是绝对正确的。第二,我不需要责备任何人,我希望避免看到偶尔出现的高相似度图形的痛苦。我写信给 SO,他们说是 Gravatar 制作了这个身份。 Gravatar 使用生成 rotational-4-symmetry 图形的算法,这些图形很有可能与 xxx 相似。他们可以使用另一个,但这是他们坚持使用的那个。即使只是镜像其中一个象限就足够了。
【解决方案2】:

在 Haskell 中,fold 由于惰性求值而能够短路求值。

但在 Scheme 中这是不可能的。这就是为什么 在 Racket 中为此提供了一个特殊的功能,ormap 能够短路评估。 IOW 它是一种特殊的fold,必须在Scheme 中显式地单独定义,因为Scheme 不是惰性的。因此,根据您的情况,我认为可以将其用作特殊的fold

使用它,我们得到

(define (diff xs ys) 
  (filter 
    (lambda (y) 
      (not (ormap (lambda (x) (equal? x y))     ; Racket's "ormap"
                  xs))) 
    ys))     

如果您的元素可以排序,最好将集合维护为有序(升序)列表;那么diff 可以更有效地实现,比较两个列表中的头元素并相应地推进,以线性时间工作。

【讨论】:

    【解决方案3】:

    使用球拍:

    (define A '(1 8 6 2))
    
    (define B '(5 7 9 1 6))
    
    (filter-not (lambda (x) (member x B)) A)
    
     ==> '(8 2)
    

    【讨论】:

    • 对不起,我忘了说我不想使用成员,只想使用地图、折叠和/或过滤器的某种组合。
    • @CodeKingPlusPlus (filter-not g xs) == (filter (lambda(x)(not (g x))) xs).
    【解决方案4】:

    当然,Scheme 中可以在 fold 之上实现 member 以在第一次匹配时短路:

    (define (member x lst)
      (call-with-current-continuation
        (lambda (return)
          (fold (lambda (e r)
                  (if (equal? e x)
                      (return #t)
                      r))
                #f lst))))
    

    【讨论】:

    • 是的,但是,短路的不是折叠。 potayto,potahto,你可能会说。 :) 仍然...
    猜你喜欢
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多