【问题标题】:How do I build up an array using let in Clojure?如何在 Clojure 中使用 let 构建数组?
【发布时间】:2021-03-31 19:06:37
【问题描述】:

(test [1 2 3]) 返回的是 [],而不是 [1 2 3],为什么?

(defn test [some-array]
      (let [x []]
        (doseq [i some-array]
          (conj x i))
        x))

(test [1 2 3])

编辑:下面的评论表明我正在尝试使用函数式语言的命令式方法,这可能是正确的。我要做的是使用初始值序列(在本例中为向量内部)作为起始数据来构建向量向量。我想我可以尝试转换向量中的每个值并返回它,但这在概念上(从命令式思维方式)似乎比获取一个起始向量并使用它来构建一个新向量并返回它更难。

例如,假设我有一个接受1 并返回"one" 的函数,并且我想让一个函数接受[1 2 3] 并返回[[1 "one"][2 "two"][3 "three"]]

【问题讨论】:

  • 我刚刚意识到我混淆了数组和向量的概念......不确定这是问题的一部分。
  • 另外,尝试for 而不是doseq 得到相同的结果......不过,我很好奇这两者之间的区别。
  • 很难猜出(至少对我而言),你究竟在追求什么。能否请您具体说明一下,您心中的转变是什么?你想mapsome-var 的每个项目还是你想缩小/增加项目等?

标签: clojure clojurescript


【解决方案1】:

Clojure 的重点是不可变数据和函数式编程。您的 那里的代码感觉就像你想在这里做一些命令式风格的想法。

您的test 也可能是identity - 一些不可变的副本 数据结构,就是不可变的数据结构本身。

如果您的示例过于简单,并且您想“附加”一些 x 里面有数据,你可以使用into

编辑

逐一转换序列(例如,每个输入的结果 不同的输出),使用map(或mapv获取向量并制作它 渴望的)。例如

(def numbers
  {1 "one"
   2 "two"
   3 "three"})

(defn test [some-array]
  (mapv #(vector % (numbers %)) some-array))

(println (test [1 2 3]))
; → [[1 one] [2 two] [3 three]]

(使用juxt作为映射函数的奖励积分)

【讨论】:

  • 这就是我感觉到的......我似乎正在重新创建一个 JS 场景。并不是说它不能在 JS 中“正确”完成……我只是说典型的 JS 方法。
  • 认为您想复制some-array,但显然没有必要这样做。您仍然可以使用into 创建一个新容器,其中包含some-array 的所有项目。
  • 我在我的原始帖子中添加了一个编辑以澄清事情(或者可能混淆事情!)。
  • 您的建议(通过您的编辑)非常简洁!哇。
  • 另外,中间变量对我来说真的很累……很高兴能在可能的时候离开let
【解决方案2】:

正如其他人所指出的,某些构建结果的方法比其他方法更容易。在这种情况下,我们看到结果向量的每个元素都是根据输入向量中的相应元素计算的。这是使用map(或mapv)的良好指示:

(def nums ["zero" "one" "two" "three" "four" "five"])

(defn arr-test [xs]
  (mapv (juxt identity nums) xs))

【讨论】:

    【解决方案3】:

    核心库中还有一个方便的实用工具clojure.pprint/cl-format 来获取数字的字符串表示(还有更多!):

    user> (def num-str (partial clojure.pprint/cl-format nil "~R"))
    #'user/num-str
    
    user> (mapv (juxt identity num-str) [0 1 2 234 2455323 1541524152352356235625])
    ;;=> [[0 "zero"]
    ;;    [1 "one"]
    ;;    [2 "two"]
    ;;    [234 "two hundred thirty-four"]
    ;;    [2455323
    ;;     "two million, four hundred fifty-five thousand, three hundred twenty-three"]
    ;;    [1541524152352356235625N
    ;;     "one sextillion, five hundred forty-one quintillion, five hundred twenty-four quadrillion, one hundred fifty-two trillion, three hundred fifty-two billion, three hundred fifty-six million, two hundred thirty-five thousand, six hundred twenty-five"]]
    

    【讨论】:

    • 哇。我以前从未见过。
    【解决方案4】:

    你真的不想那样做 - 但如果你做过 :-) 你会想使用 loop 而不是 let:

    (defn arr-test [some-array]
      (let [digit-words {1 "one", 2 "two",   3 "three", 4 "four", 5 "five",
                         6 "six", 7 "seven", 8 "eight", 9 "nine", 0 "zero" }]
        (loop [x []
               a some-array]
          (if (empty? a)
            x
            (recur (conj x [(first a) (digit-words (first a))]) (rest a))))))
    

    更好的方法是:

    (defn arr-test [some-array]
      (let [digit-words {1 "one", 2 "two",   3 "three", 4 "four", 5 "five",
                         6 "six", 7 "seven", 8 "eight", 9 "nine", 0 "zero" }]
        (mapv #(vector % (digit-words %)) some-array)))
    

    【讨论】:

    • 这很容易知道......但是,看起来我应该采用函数式方法。我也刚刚读到我应该避免使用数组而不是向量,除非我需要 Java 生态系统中的特定内容。谢谢!
    • “recur loop”解决方案是我开始学习Clojure时首先想到的,所以我认为它是“开始Clojure”。 “这是 Clojure,Jim - 但不是我们所知道的!”。 :-) 第二个是“更好的方法”——使用函数来完成工作,这也更清楚(一旦你知道所有这些 dang 函数的作用:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 2018-03-10
    • 2021-04-07
    • 2015-10-18
    • 2015-06-04
    • 1970-01-01
    • 2015-08-26
    相关资源
    最近更新 更多