【问题标题】:Arrays and loops in ClojureClojure 中的数组和循环
【发布时间】:2018-05-03 14:22:11
【问题描述】:

我正在尝试学习编程语言 Clojure。我很难理解它。我想尝试实现像这样简单的东西(例如):

int[] array = new int[10];
array[0] =1; 
int n = 10; 

for(int i = 0; i <= n; i++){
    array[i] = 0; 
}
return array[n]; 

由于我是 Clojure 的新手,我什至不明白如何实现它。如果有人能解释或给出一个类似的例子来说明数组/for-loops 在 Clojure 中是如何工作的,那将是非常有帮助的。我试图做一些研究,但就我的理解而言,Clojure 并没有真正的 for 循环。

【问题讨论】:

  • 你要翻译的代码很奇怪。它包含许多会导致运行时错误的错误,尝试读取和写入超出范围的array[10]。此外,它初始化整个数组,然后将其丢弃并返回单个元素。如果你想翻译代码,如果你翻译的代码有清晰的特征会更容易。

标签: clojure


【解决方案1】:

编写您编写的Java for 循环的Clojure 方法是首先考虑您为什么要循环。将 Java 循环移植到 Clojure 有很多选择。在其中进行选择取决于您的目标。

制作十个零的方法

正如 Carcigenicate 所发布的,如果您在一个集合中需要十个零,请考虑:

(repeat 10 0)

这会返回一个sequence – 一个lazy。序列是 Clojure 的核心抽象之一。如果需要通过索引访问十个零,请将它们放在一个向量中:

(vec (repeat 10 0))

(into [] (repeat 10 0))

或者,您可以直接在代码中编写向量字面量:

[0 0 0 0 0 0 0 0 0 0]

如果您出于某种原因特别需要一个 Java 数组,那么您可以使用to-array

(to-array (repeat 10 0))

但请记住the Clojure reference docs on Java interop的建议:

Clojure 支持创建、读取和修改 Java 数组 [但] 建议您限制使用数组以与需要它们作为参数或将它们用作返回值的 Java 库进行互操作。

这些文档列出了一些用于处理 Java 数组的函数,主要用于 Java 互操作或“支持突变或更高性能的操作”。在几乎所有情况下,Clojurists 只使用向量。

在 Clojure 中循环

如果您要做的不是产生十个零,该怎么办? Clojure 的“循环”方式取决于您的需要。

你可能需要递归,Clojure 有 loop/recur:

(loop [x 10]
  (when-not (= x 0)
    (recur (- x 2))))

您可能需要某些集合中的每个值计算一个值:

(for [x coll])
  (calculate-y x))

您可能需要遍历多个集合,类似于 Java 中的嵌套循环:

(for [x ['a 'b 'c] 
      y [1 2 3]]
  (foo x y))

如果您只需要多次产生副作用,repeatedly 就是您的果酱:

(repeatedly 10 some-fn)

如果您需要为集合中的每个值产生副作用,请尝试doseq

(doseq [x coll]
  (do-some-side-effect! x))

如果您需要对整数范围产生副作用,您可以像这样使用doseq

(doseq [x (range 10)] 
  (do-something! x))

...但是dotimes 就像带有内置rangedoseq

(dotimes [x 9] 
  (do-something! x))

比那些循环构造更常见的是 Clojure 函数,它为集合中的每个元素生成一个值,例如 map 及其亲属,或者出于特殊目的迭代集合(例如 filter 和 @987654349 @) 或创建一些新值或集合(如reduce)。

【讨论】:

    【解决方案2】:

    我认为@Lee 回答中的最后一点应该大力强调。

    除非您出于性能原因绝对需要数组,否则您确实应该在这里使用向量。一旦你开始使用原生 Clojure 结构,整个代码块就会突然变得微不足道:

    ; Create 10 0s, then put them in a vector
    (vec (repeat 10 0))
    

    如果您可以使用惰性序列而不是严格向量,您甚至可以跳过对 vec 的调用。

    还应该注意的是,将元素预初始化为 0 是不必要的。与数组不同,向量是可变长度的;它们可以在创建后添加和扩展。只拥有一个空向量并根据需要向其中添加元素要干净得多。

    【讨论】:

      【解决方案3】:

      您可以在 clojure 中创建和修改数组,您的示例如下所示:

      (defn example []
        (let [arr (int-array 10)
              n 10]
          (aset arr 0 1)
          (doseq [i (range (inc n))]
            (aset arr i 0))
          (aget arr n)))
      

      请注意,此示例将引发异常,因为array[i]i = 10 时超出范围。

      您通常会优先使用向量而不是数组,因为它们是不可变的。

      【讨论】:

      • 哦,太好了!非常感谢!!我一定会试试这个。是否可以在不定义数组大小的情况下创建数组?
      • @Anna - 数组是固定长度的容器,因此您必须在创建时指定大小。如果你想要一个可变大小的容器,你可以使用 clojure 的 vectorlist 类型,或者 Java 的 ArrayList
      • 请记住,这种方法虽然性能更好,但在日常使用中并不完全是惯用的 Clojure。 @Carcigenicate 的示例更像普通的 Clojure。我们处理不可变值的转换,这就是为什么没有很多循环结构的原因。在 Clojure 中,您可以编写诸如 map 和 filter 之类的函数来获得结果。这是一个真正的思维转变,但很有趣! :D
      【解决方案4】:

      您可以从这里开始:

      【讨论】:

      • 感谢您的提示!我已经看过 CheatSheet,但也会看一下 Brave Clojure :)
      猜你喜欢
      • 2012-03-05
      • 2012-01-30
      • 1970-01-01
      • 2010-11-06
      • 2020-10-28
      • 2023-02-02
      • 1970-01-01
      • 2021-02-13
      相关资源
      最近更新 更多