【问题标题】:Emacs Lisp: How to sum odd numbers in a list?Emacs Lisp:如何对列表中的奇数求和?
【发布时间】:2014-03-18 19:14:04
【问题描述】:

我想找到 elisp 的类似物:

sum(n for n in numbers if n % 2) # Python
numbers.select { |n| n % 2 != 0 }.inject { |a, b| a + b } # Ruby

命令式:

(defun oddp (number)
  (not (= (mod number 2) 0)))

(defun sum-odd-with-dolist (list)
  (let ((acc 0))
    (dolist (item list acc)
      (if (oddp item)
          (setq acc (+ item acc))))))

来自Porting Common Lisp

(defun sum-odd-with-dolist-incr (list)
  (let ((total 0)) 
    (dolist (item list) 
      (if (oddp item)
          (incf total item))) 
      total))

使用'cl-*'loop

(defun sum-odd-with-loop (list)
  (loop for x in list if (oddp x) sum x))

(sum-odd-with-loop '(1 2 3))
4

有没有更惯用的方法(不需要cl-* 包)?

相关:

How to sum a list of numbers in Emacs Lisp?

【问题讨论】:

    标签: emacs elisp


    【解决方案1】:

    惯用的方法是使用cl 包中的函数和宏。它们是 Emacs Lisp 的标准配置,使用它们没有任何问题。

    我怀疑你会找到一种简洁明了的方法

    (loop for x in list if (oddp x) sum x)
    

    还有更多功能性的方法可以做到,比如

    (apply #'+ (remove-if-not #'oddp list))
    

    但这使用来自cl-seq 包的remove-if-not。你可以手动写一个循环:

    (let ((sum 0)) (dolist (x list sum) (when (oddp x) (incf sum x))))
    

    但这使用了dolistincf,它们都在cl-macs 包中。基本上你无法逃脱cl 包:oddp 本身就是一个cl 函数!

    我完全不使用cl 设施的最大努力是:

    (apply #'+ (mapcar (lambda (x) (* x (mod x 2))) list))
    

    但在实践中使用它而不是 (loop ...) 版本会很荒谬。

    【讨论】:

    【解决方案2】:
    
    (apply '+ (delq nil (mapcar (lambda (x) (and (= 1 (% x 2)) x)) '(1 2 3 4 5))))
    

    【讨论】:

    • 如果第一个参数是偶数,它是否复制整个列表?
    • 没有参数,我只写了一个表达式。它的作用是:过滤列表,然后应用 '+.过滤确实会构建新列表。无论如何,结果不依赖于第一个参数是偶数还是奇数。所以我不确定我是否理解你的问题..
    • s/argument/列表的第一个元素/。 delq 的文档说它不能就地删除第一个元素(或者我误解了一些东西)。
    • delq 我完全错了,如果“object”不是第一个元素,它可能会修改列表。那么, remq 是否合适,因为它会创建一个新列表?
    • 虽然,mapcar 只为 delq/remq 构建新列表,所以原始列表保持不变。
    【解决方案3】:

    我同意Gareth Rees's answer,但如果您在代码中大量使用函数式编程和数据操作,我强烈建议安装 Magnar Sveen 的dash.el 列表 API。它有大量用于编写简洁优雅的函数式代码的函数。顺便说一句,它本身是在没有cl 库的情况下编写的。总结一个列表中的奇数将是:

    (-sum (--filter (= (mod it 2) 1) '(1 2 3 4 5)))
    

    -sum(-reduce '+ xs) 的缩写。以 2 个连字符开头的函数是 anaphoric macros,它们公开了一个临时变量。例如,这里不是将(lambda (x) (= (mod x 2) 1)) 传递给--filter,而是简单地写成(= (mod it 2) 1),其中it 用于局部变量。

    【讨论】:

    • 更简洁的方式:(-sum (-filter #'oddp '(1 2 3 4 5))),或者我最喜欢的,使用线程宏:(->> '(1 2 3 4 5) (-filter #'oddp) (-sum))
    • 好收获!但是oddp来自cl.el,所以如果由于某种原因你没有在你的代码中使用它,你必须使用(= (mod it 2) 1)。您是否介意我将您的评论整合到我的答案中(带有署名)?
    • 没问题,无论是否注明出处,都可以随意整合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多