【问题标题】:Clojure: How do I have a for loop stop when the value of the current variable matches that of my input?Clojure:当当前变量的值与我的输入值匹配时,我如何让 for 循环停止?
【发布时间】:2014-10-08 15:50:40
【问题描述】:

前言 首先,我是 Clojure 和编程的新手,所以我想我会尝试创建一个函数,使用我的自然直觉来求解一个非平凡的方程。结果是希望找到平方根。

问题 阻止我的 square-n-map-maker 函数迭代超过某个点的最有效方法是什么?我想修复 square-n-map-maker 以便我可以注释掉 square-maker 函数,该函数为我提供了我当前想要查看的结果和格式,但无法回忆平方根答案(到目前为止据我所知)。

即我希望它在大于或等于我的输入值时停止

我最初的想法是,我希望它不是一个关键字列表,而是一个地图。但是我很难让我的功能给我一张地图。我想要一张地图的全部原因,其中一对中的一个成员是 n,另一个是 n^2,以便我可以从中提取实际的平方根并将其作为答案返回给用户。

关于实现此目的的最佳方法有什么想法吗? (下面是我要修复的功能)

;; attempting to make a map so that I can comb over the 
;; map later and recall a value that meets
;; my criteria to terminate and return result if (<= temp-var input)

(defn square-n-map-maker [input] (for [temp-var {remainder-culler input}]
                                   (map list(temp-var) (* temp-var temp-var))
                                 )
)
(square-n-map-maker 100) => clojure.lang.ArityException: Wrong number of args (0) passed to: MapEntry
         AFn.java:437 clojure.lang.AFn.throwArity
          AFn.java:35 clojure.lang.AFn.invoke

/Users/dbennett/Dropbox/Clojure Files/SquareRoot.clj:40 sqrt-range-high-end/square-n-map-maker[fn]

以下是我的其余代码

;; My idea on the best way to find a square root is simple.
;; If I want to find the square root of n, divide n in half
;; Then find all numbers in 0...n that return only a remainder of 0.
;; Then find the number that can divide by itself with a result of 1.
;; First I'll develop a function that works with evens and then odds
(defn sqrt-range-high-end [input] (/ input 2))
(sqrt-range-high-end 100) => 50

(defn make-sqrt-range [input] (range (sqrt-range-high-end (+ 1 input))))
(make-sqrt-range 100) =>(0 1 2 3 4 5 6 ... 50)    
(defn zero-culler [input] (remove zero? (make-sqrt-range input)))
(zero-culler 100) =>(1 2 3 4 5 6 ... 50) 

(defn odd-culler [input] (remove odd? (zero-culler input)))
(odd-culler 100) => (2 4 6 8 10...50)    

(defn even-culler [input] (remove even? (zero-culler input)))
(even-culler 100) => (1 3 5 7...49)

(defn remainder-culler [input] (filter #(zero? (rem input %)) (odd-culler input)))
(remainder-culler 100) => (2 4 6 12 18)

(defn square-maker [input] (for [temp-var (remainder-culler input)]
                           (list (keyword (str
                                             temp-var" "
                                             (* temp-var temp-var)
                                           )
                                 )
                           )
                       )
(square-maker 100) => ((:2 4) (:4 16) (:10 100) (:20 400) (:50 2500))

【问题讨论】:

    标签: loops for-loop clojure square-root


    【解决方案1】:

    阅读错误信息!

    你有点超前了!您的错误与让for 停止“循环”无关。

    (defn square-n-map-maker [input] (for [temp-var {remainder-culler input}]
                                       (map list(temp-var) (* temp-var temp-var))))
    (square-n-map-maker 100) => clojure.lang.ArityException: Wrong number of args (0) passed to: MapEntry
              AFn.java:437 clojure.lang.AFn.throwArity
              AFn.java:35 clojure.lang.AFn.invoke
    

    注意错误信息。他们是你的朋友。在这种情况下,它告诉您将错误数量的参数传递给MapEntry(搜索IPersistentMap)。那是什么?

    {} 创建一个映射文字。 {:key :value :key2 :value2} 是一张地图。地图可以像函数一样使用:

     > ({:key :value} :key)
     :value
    

    访问映射中与键关联的条目。现在,您在第一行创建了一个地图:{remainder-culler input}。您刚刚将函数剩余剔除器映射到输入。如果您从地图中抓取一个项目,它就是MapEntry。每个 MapEntry 都可以用作函数,接受索引作为参数,就像 Vector 一样:

    > ([:a :b :c :d] 2)
    :c
    

    您的 for 正在遍历 {remainder-culler input} 中的所有 MapEntries,但只有一个:[remainder-culler input]。这个 MapEntry 被分配给temp-var

    然后在下一行中,您将该地图括在括号中:(temp-var)。这形成了一个S-expression,并且假设表达式中的第一项是函数/过程,则计算表达式。所以它需要一个索引(这里的有效索引是 0 和 1)。但是你没有向temp-var 传递任何参数。因此:clojure.lang.ArityException: Wrong number of args.

    另外,请注意map 不是Map 的构造函数。

    构建地图

    现在,谈谈你的问题。您的 square-maker 正在返回一个格式正确的地图列表,但它是由嵌套列表组成的。

    试试这个:

    (apply hash-map (flatten (square-maker 100)))
    

    阅读this pagethis page 了解其工作原理。

    如果您不介意切换键和值的顺序,可以使用group-byI mentioned before

    (defn square-maker [input]
        (group-by #(* % %) (remainder-culler input)))
    (square-maker 100) => {4 [2], 16 [4], 100 [10], 400 [20], 2500 [50]}
    

    然后你可以像这样获取你需要的值:(first ((square-maker 100) 100))。这使用了我上面提到的 map-as-function 功能。

    循环

    如果你真的想坚持直观的循环概念,我会使用loop,而不是forfor 是懒惰的,这意味着既没有手段也没有理由(如果你正确使用它)来“停止”它——除非你从它那里请求一个值,否则它实际上不会做任何工作,它只会做它必须为您提供您所要求的价值。

    (defn square-root [input]
        (let [candidates (remainder-culler input)]
             (loop [i 0]
                (if (= input (#(* % %) (nth candidates i)))
                    (nth candidates i)
                    (recur (inc i))))))
    

    嵌入的if 决定循环何时停止。

    但请注意,loop 仅返回其最终值(如果这句话对您没有意义,请熟悉loop 的文档)。如果你想建立一个哈希映射供以后分析,你必须做类似(loop [i 0, mymap {}] ...的事情。但是,如果可以立即完成,为什么要稍后分析呢? :-)

    现在,这是一个非常脆弱的平方根函数,让它陷入无限循环并不难(喂它 101)。我把它作为练习留给你来解决(无论如何,这都是一个学术练习,对吧?)。

    我希望这能再次帮助您。我认为这是学习一门新语言的一个大问题。不过,我应该说,作为记录,一旦您对自己的解决方案感到满意,您应该search for other Clojure solutions 解决问题,看看您是否能理解它们的工作原理——这可能是“直观的”,但它是不太适合 Clojure 的工具和功能。查看其他解决方案将帮助您更好地了解 Clojure 的世界。

    更多阅读:

    Imperative looping with side-effects.

    How to position recur with loop

    The handy into

    最后,this "not constructive" list of common Clojure mistakes

    【讨论】:

    • 这真的很有帮助,谢谢你在这里提供的详细解释。
    【解决方案2】:

    for 不是循环,也不是迭代。它懒惰地创建一个列表推导,并且只在需要时才实现值(在这种情况下,当 repl 尝试打印评估结果时)。有两种常用的方法来做你想做的事:一种是将square-maker 包裹在

    (first (filter some-predicate (square-maker number))) 获取序列中第一个符合 some-predicate 的元素。例如

    (first (filter #(and (odd? %) (&lt; 50 %)) (range))) => 51

    显然,上面不会实现无限范围。

    另一种方法是不使用列表推导式,而是以更命令式的方式进行:运行带有终止条件的实际循环(参见looprecur)。

    例子:

    (loop [x 0]
      (if (and (odd? x) (> x 50))
        x
        (recur (inc x))))
    

    【讨论】:

      猜你喜欢
      • 2021-06-19
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2017-02-02
      • 1970-01-01
      • 2016-12-28
      • 1970-01-01
      • 2017-12-21
      相关资源
      最近更新 更多