【问题标题】:Scheme - sum the squares of even-valued elements in a listScheme - 对列表中偶数元素的平方求和
【发布时间】:2018-01-29 13:00:32
【问题描述】:

我希望能够对列表中偶数元素的平方求和,但是我当前的代码只对元素求和,而不是平方。有谁知道可以进行任何修改以对列表中偶数元素的平方求和?

(define (sum elemList)
  (if
    (null? elemList)
    0
    (+ (car elemList) (sum (cdr elemList)))
  )
)

我的意见是:

(sum-evens (list 1 2 3 4))

输出将是:

20

这是(2*2) + (4*4)

如果可能,最好同时看到递归和迭代解决方案。 有什么想法吗?

【问题讨论】:

  • “偶数元素”是指出现在偶数索引处的元素吗?或者你的意思是那些本身是偶数的元素?
  • @pnkfelix 我的意思是可以被 2 整除的元素(偶数不是索引)

标签: list scheme filtering singly-linked-list fold


【解决方案1】:

配合Racket的左折叠功能,

(define (foldl cons z ls) 
  (if (null? ls) 
    z 
    (foldl cons (cons (car ls) z)           ; NB! args order
                (cdr ls))))   

我们可以轻松实现列表的求和 ((foldl + 0 xs)), 取平方, 分别进行过滤。

嵌套它们也很容易,以便一个与另一个的结果一起使用(如其他答案所示),但这意味着 三个 执行单独的列表遍历,

(define (sqr x) (* x x)) 

(define (foo-3 xs)
  (foldl  +  0 
    (foldl  (lambda (x acc) (cons (sqr x) acc))  '() 
      (foldl  (lambda (x acc) (if (even? x) (cons x acc) acc))  '() 
        xs))))

但实际上,使用 reducer 函数(如 +)折叠(或“reducing”)列表会替换列表的cons 无论如何都要使用那个函数的应用程序,那么为什么不直接使用那个 reducer 呢?这意味着嵌套的折叠可以融合在一起为

(define (foo-2 xs)
    (foldl  (lambda (x acc) (+  (sqr x) acc))  0 
      (foldl  (lambda (x acc) (if (even? x) (cons x acc) acc))  '() 
        xs)))

还有,作为

(define (foo-1 xs)   ; one traversal, tail-recursive, iterative!
      (foldl  (lambda (x acc) (if (even? x) (+ (sqr x) acc) acc))  0 
        xs))

因此派生 迭代单遍历函数,否则可以相对容易地手动编码,但作为递归变体(见在其他答案中)。

我们看到这里有必要抽象cons,这样就可以很容易地操作、替换它。换句话说,我们想要

  (lambda (x acc) (cons (sqr x) acc))               ==:  ((mapping  sqr) cons)
  (lambda (x acc) (if (even? x) (cons x acc) acc))  ==:  ((filtering even?) cons)

从而得到所谓的“转换器”,即reducer 函数的修饰符:

(define (((mapping  f) kons) x acc) (kons (f x) acc))           ; "mapping" transducer
(define (((filtering p) kons) x acc) (if (p x) (kons x acc) acc))    ; "filtering" one

(define (foo xs)

  (foldl + 0 
    (foldl ((mapping sqr) cons) '() 
      (foldl ((filtering even?) cons) '() 
        xs)))
  =
    (foldl ((mapping sqr) +) 0                 ; replace the constructor!
      (foldl ((filtering even?) cons) '()      ;   and the sentinel value
        xs))
  =
      (foldl ((filtering even?) ((mapping sqr) +)) 0    ; and again!
           ; (lambda (x acc) (if  (even? x)  (+ (sqr x) acc)  acc)) ; look, ma, no cons!
        xs)
  )

(f (g x)) 模式被抽象为功能组合

(define ((compose1 f g) x)
    (f (g x)))

所以(f (g x))((compose1 f g) x)。使用更通用的compose 接受任何个要组合的函数,

(define ((compose . fs) x)
  (if (null? fs)
    x
    ((car fs) ((apply compose (cdr fs)) x))))

我们可以用更通用的方式对其进行编码,将我们可能需要的许多转换器组合到一个组合转换器中,为它输入的每个参数执行组合操作(取自输入序列;这里是一个列表):

(define (transduce-list tducr op z xs)
  (foldl                            ; lists are reduced with foldl
         (tducr op)                 ; the full reducer is created by applying
         z xs))                     ;   the transducer to the final reducer

(define (foo xs)                    ; sum the squares of evens in a list
  (transduce-list                   ; by transducing the list
       (compose                     ;   with the transducer composed from
           (filtering even?)        ;     a filtering step and
           (mapping sqr))           ;     a mapping step
       + 0 xs))                     ;   with + as the final reducer

就是这样!


所以我们有

(define (sqr1 x) (+ 1 (* x x)))     ; for clearer testing results

> (foldl ((mapping sqr1) cons) '() '(1 2 3 4))
'(17 10 5 2)
> (foldl ((mapping sqr1) +) 0 '(1 2 3 4))
> 34

((mapping sqr1) cons),就像cons 本身一样,是一个有两个参数的函数,因此可以用作foldlreducer 函数 参数。

(define g ((mapping sqr1) cons))

相同
(define (g x acc)
      (cons (sqr1 x) acc)) 

filtering 我们有

> (foldl ((filtering even?) +) 0 '(1 2 3 4))
> 6
> (foldl ((mapping sqr1) ((filtering even?) cons)) '() '(1 2 3 4))
> '(10 2)
> (foldl ((filtering even?) ((mapping sqr1) cons)) 0 '(1 2 3 4))
> '(17 5 . 0)

所以,((mapping sqr1) ((filtering even?) cons)) 是一个减速器,(mapping sqr1) 使用 ((filtering even?) cons) 作为 减速器。而 that(filtering even?) 使用 cons 作为 its - 链中的最终 - 减速器功能:

(define g
  ((mapping sqr1) ((filtering even?) cons)))
=
(define (g x acc)
  (let ((f ((filtering even?) cons)))
    (f (sqr1 x) acc)))                          ; by definition of mapping
= 
(define (g x acc)
  (define (f y acc)
    (if (even? y)  (cons y acc)  acc))          ; by definition of filtering
  (f (sqr1 x) acc))
= 
(define (g x acc)
  (let ((y (sqr1 x)))
    (if (even? y)  (cons y acc)  acc)))          ; by application rule

嗯,映射、过滤和 consing 都自动为我们汇总到 一个 reducer 函数中,就好像我们自己编写的一样!更好的是,foldl 是尾递归的,整个函数是迭代的,并且只执行一次列表遍历——因为三个 reducer 函数合并为一个。

更多测试:

(define (bar xs)
  (foldl ((compose
                (filtering even?)    ; filtering is done first
                (mapping sqr1))
           cons)
          0 xs))

(define (baz xs)
  (foldl ((compose
                (mapping sqr1)       ; mapping is done first
                (filtering even?))
           cons)
         '() xs))

这样

> (bar '(1 2 3 4 5))
'(17 5 . 0)
> (baz '(1 2 3 4 5))
'(26 10 2)

【讨论】:

  • sick 粗体 代码块中的技巧
  • @naomik 非常感谢,我很感激。令人尴尬的是,我真的做到了,发现它们,对我来说,在这里(1)。我搜索并尝试阅读了很多内容,但这只是一堵无法穿透的迷人魔法文字墙。而对于 Haskellers,情况恰恰相反,是难以理解的类型魔法。实际上,如果只使用代码而不是文字,那是非常清楚的。 :) -- (1) 差不多;首先我意识到 - 在 Haskell 中 - transduce xf op z xs = foldr (xf op) z xs - 甚至没有人告诉你这个最简单的一点! - 但在这里我意识到这一切都来自 fold fusion.
  • 别觉得尴尬——我已经跟踪你很久了,我知道你有多聪明。我更新了我的答案,以包括我对换能器的理解,以便它可以进一步帮助您。希望对你有帮助^_^
  • 谢谢你,你真好。我认为您高估了我,:) 您只需查看这篇文章的编辑历史即可了解原因。但我正在努力。 :)
【解决方案2】:

或使用 SICP 样式流!

如果您对此感兴趣,请参阅计算机程序的结构和解释(Sussman,Abelson)的 3.5 部分

这里的流函数的工作方式与@WillNess 的答案中描述的转换器非常相似——即,流不需要通过数据进行多次迭代

这里的所有流过程都是递归的,但演变为线性迭代过程

值得注意的是,cons-stream 是一种特殊形式,不会立即计算它的第二个参数

#lang sicp

(define (square x)
  (* x x))

(define stream-car car)

(define (stream-cdr s)
  (force (cdr s)))

(define (integers x)
  (cons-stream x (integers (inc x))))

(define (stream-filter f s)
  (cond ((stream-null? s) the-empty-stream)
        ((f (stream-car s)) (cons-stream (stream-car s) (stream-filter f (stream-cdr s))))
        (else (stream-filter f (stream-cdr s)))))

(define (stream-map f s)
  (if (stream-null? s)
      the-empty-stream
      (cons-stream (f (stream-car s)) (stream-map f (stream-cdr s)))))

(define (stream-take n s)
  (cond ((stream-null? s) the-empty-stream)
        ((> n 0) (cons-stream (stream-car s) (stream-take (dec n) (stream-cdr s))))
        (else the-empty-stream)))

(define (stream-reduce f acc s)
  (if (stream-null? s)
      acc
      (stream-reduce f (f acc (stream-car s)) (stream-cdr s))))

(stream-reduce + 0
  (stream-map square
    (stream-filter even?
      (stream-take 10
        (integers 1)))))
;; => 220

传感器

我怀着极大的感情为@WillNess 提出这部分答案

我是introduced to transducers 的人,他擅长将极其复杂的事物提炼成奇迹般的简单事物——作为一种热爱的劳动,我改编了一些提出的代码/想法(最初在 javascript 中)来计划

每个;; [section] 定义一个抽象屏障

编辑:删除了 cons-contcons-trans 的特殊形式——宏并没有显着提高代码的可读性

#lang sicp

;; [procedure]
(define (compose f g)
  (lambda (x) (f (g x))))

;; [list]
(define (foldl f acc xs)
  (if (null? xs)
      acc
      (foldl f (f acc (car xs)) (cdr xs))))

;; [continuation]
(define cons-cont
  identity)

(define the-empty-cont
  identity)

(define cont-concat
  compose)

(define (cont-concat-all cs)
  (foldl cont-concat the-empty-cont cs))

;; [trans]
(define (cons-trans f)
  (cons-cont (lambda (cont) (lambda (acc x) (f cont acc x)))))

(define the-empty-trans
  the-empty-cont) ;; unused in this program, but completes implementation

(define trans-concat
  cont-concat)    ;; unused in this program, but completes implementation

(define trans-concat-all
  cont-concat-all)

;; [transducer]
(define (cons-transducer . ts)
  (lambda (f acc xs)
    (foldl ((trans-concat-all ts) f) acc xs)))

(define (mapper f)
  (cons-trans (lambda (next acc x) (next acc (f x)))))

(define (filterer f)
  (cons-trans (lambda (next acc x) (if (f x) (next acc x) acc))))

(define (effector f)
  (cons-trans (lambda (next acc x) (f x) (next acc x))))

(define (logger s)
  (effector (lambda (x) (display s) (display " ") (display x) (display "\n"))))

;; [main]
(define (square x)
  (* x x))

(define (main xs)
  ((cons-transducer (logger "input")
                    (filterer even?)
                    (logger "filtered")
                    (mapper square)
                    (logger "squared"))
   + 0 xs))

(main '(1 2 3 4 5 6 7 8 9 10))

输出

input 1
input 2
filtered 2
squared 4
input 3
input 4
filtered 4
squared 16
input 5
input 6
filtered 6
squared 36
input 7
input 8
filtered 8
squared 64
input 9
input 10
filtered 10
squared 100
=> 220

【讨论】:

  • love 流...不同之处在于,对于传感器,实际上只有一个处理功能,one 组合(...或composed) “减速器”。那么数据源的类型由使用中的折叠类型决定——一个用于列表,另一个用于通道(显然)等。结果的类型由最终的减速器操作决定(如+)- - 但换能器是相同的。所以真的很好;给了我们新的语言。 Rich Hickey 是一个非常聪明的人。 :) -- 一般来说,我对“数据流”范式非常感兴趣。流感觉像是隐式 DF,tducers 喜欢更显式的 DF。
  • @WillNess 您在此处的评论对我来说非常有意义,并且在我的答案的更新中得到了很好的体现(我希望)——无关:令我惊讶的是,cont-concat 相当于 (lambda (f g) (lambda (x) (f (g x))),相当于致compose——我对此的迷恋与我的困惑相匹配
  • @WillNess 我认为宏实际上损害了该程序的可读性——我进行了删除它的编辑。结果,cont 的简化实现真的让我大吃一惊
  • 我现在已经完成了 Scheme 代码,它产生的定义与我的大致相同。 :) JS,我不太懂。 :)
  • 是的,从概念上讲,这最终归结为folds 中的fold,对吧?谢谢分享,威尔^_^
【解决方案3】:

有两种可能,要么我们从头实现递归:

(define (sum elemList)
  (cond ((null? elemList) 0)
        ((even? (car elemList))
         (+ (* (car elemList) (car elemList))
            (sum (cdr elemList))))
        (else (sum (cdr elemList)))))

或者我们使用内置程序,根据需要定义帮助程序。这种策略被称为使用"sequences as conventional interfaces"

(define (square x)
  (* x x))

(define (sum elemList)
  (apply +
         (map square
              (filter even? elemList))))

在 Scheme 中,首选方法是第二种方法,因为当我们拥有已经为我们完成这项工作的程序时,我们不能重新发明轮子。无论哪种方式,它都按预期工作:

(sum '(1 2 3 4 5 6 7 8 9 10))
=> 220

【讨论】:

  • 解释得很好,我真的很感激。正是我想要的:)
【解决方案4】:
(define (sum ls)
  (if (null? ls)
      0
      (if (even? (car ls))
          (+ (square (car ls)) (sum (cdr ls)))
          (sum (cdr ls)))))

在哪里

(define (square x)
  (* x x))

对偶数元素的平方求和。如果你不做任何事情就对列表的元素求和,那么答案当然不能是你问题的答案。

此外,可以这样实现这个过程:

(define (sum ls)
  (reduce +
          0
          (map square
               (filter even?
                       ls))))

其中mapfilterreduce是常用的意思(可以在mit-scheme中试试)。这做同样的事情,但是这更具可读性,并且优化了 cdr 递归之类的东西。 SICP第二章(结构与解释) of Computer Programs) 介绍了这种编程方法。

【讨论】:

  • 感谢您的回复,也是一个很好的解决方案:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-24
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多