【问题标题】:Defining my own max function with variable arguments用可变参数定义我自己的 max 函数
【发布时间】:2011-09-21 16:07:21
【问题描述】:

我正在学习 Clojure 解决4clojure 中列出的问题。练习之一是使用可变参数创建您自己的 max 函数。

我正在尝试使用 REPL 解决这个简单的问题,我得到了这个解决方案:

(defn my-max 
    [first & more] (calc-max first more))

(defn calc-max 
    [m x] 
        (cond (empty? x) m
            (> (first x) m) (calc-max (first x) (rest x))
            :else calc-max m (rest x)))

这很好,但练习不允许使用def,因此我必须将这两个功能合二为一。当我用它的代码替换 calc-max 引用时,结果是:

(defn my-max 
    [first & more] 
    ((fn calc-max 
        [m x] 
            (cond (empty? x) m
                (> (first x) m) (calc-max (first x) (rest x))
                :else calc-max m (rest x)))
                    first more))

但是这段代码不起作用并返回下一个错误:

user=> (my-max 12 3 4 5 612 3)
java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)

我猜这个错误来自于尝试评估 calc-max 函数的结果,我猜这是我的语法错误,但我不知道如何解决它。

【问题讨论】:

  • 对于它的价值,我喜欢将我的 4clojure 问题的助手定义为包裹结果的letfn,最终与分离的defns 具有相同的语义。例如,(letfn [(calc-max [m x] ...)] (fn my-max [first & more] ...))Now first 的两个实例在词法上是分开的,不会发生冲突,您不必对任何一个函数进行任何重写。

标签: clojure variadic-functions


【解决方案1】:

这是我用来解决它的函数。关键是根本不使用 max 。

(fn [& args] (reduce (fn [x y] (if (> x y) x y) ) args ) )

【讨论】:

  • 聪明而简短的回答,太棒了!
【解决方案2】:

真正的错误是您调用了参数first - 它将真正的first 函数重新绑定到数字! 只需将名称更改为其他名称,您的变体就可以工作。尽管显式命名函数可能更好,而不是调用匿名函数,例如,您可以使用 letfncalc-max 声明为本地函数。所以你的my-max 看起来像:

(defn my-max [ff & more]
  (letfn [(calc-max [m x] 
            (cond (empty? x) m
                  (> (first x) m) (calc-max (first x) 
                                            (rest x))
                  :else (calc-max m (rest x))))]
    (calc-max ff more)))

虽然我认为你可以编写更简单的代码:

(defn my-max [& more] (reduce max more))

【讨论】:

  • 哇,第一次完全错过了,我被这本书第 32 页的例子注定要失败,它显示了现在我知道的 var arg 函数是错误的!
  • 你甚至可以使用 max 编写一个更简单的代码: (max 12 3 3 441 123) 但练习不会让你使用核心的 max 函数 ;)
  • 然后你可以用 2-args 函数替换它,比如:#(if (> %1 %2) %1 %2)
【解决方案3】:

您的函数不起作用,因为 fn 中的 first 被视为函数而不是输入值。所以当你写

user=> (my-max 12 3 4 5 612 3)

无法将 12 投射到功能上,这真是太高了。简单来说,可以改写为

(defn my-max1 [fst & more]
  ((fn calc-max [m x]
     (cond (empty? x) m
           (> (first x) m) (calc-max (first x) (rest x))
           :else (calc-max m (rest x))))
    fst more))

甚至没有fn

(defn my-max [x & xs]
  (cond (empty? xs) x
        (> (first xs) x) (recur (first xs) (rest xs))
        :else (recur x (rest xs))))

【讨论】:

    【解决方案4】:

    详细说明您看到的异常:每当 Clojure 抛出类似

    java.lang.Integer cannot be cast to clojure.lang.IFn
    

    对你来说,这意味着它试图调用一个函数,但它试图调用的不是函数而是别的东西。当您有这样的代码时,通常会发生这种情况

    (smbl 1 2 3)
    

    如果smbl 引用了一个函数,clojure 将使用参数 1 2 和 3 执行它。但是如果 smbl 没有引用一个函数,那么您将看到类似于上面的错误。这是我查看您的代码的指针,正如 4e6 指出的那样,(first x) 是这里的罪魁祸首,因为您将函数参数命名为 first

    【讨论】:

      【解决方案5】:

      不如reduce,但还可以:

      (fn [& 参数] (循环 [l args, maxno (第一个 args)] (如果(空?l) 最大值 (if (> maxno (first l) ) (recur (rest l) maxno) (recur (rest l) (first l))))))

      我想可以使用 cond

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-11
        • 1970-01-01
        • 2010-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多