我对Clojure和函数式编程非常陌生,我想知道如何执行以下操作:
我有一个名为s
的向量矢量,看起来像这样:
user=> (println s)
[[1 1 1 3] [2 2 2 3] [3 2 1 1] [4 3 3 4]]
我想要做的是'循环'通过s
的每个内部向量,并根据要替换的数字将第二个元素更改为不同的字符串。最终结果可能是这样的:
; Hopeful Result:
user=> (println new-s)
[[1 "Joe" 1 3] [2 "Fred" 2 3] [3 "Fred" 1 1] [4 "Martha" 3 4]]
我到目前为止的这个想法是使用for
循环遍历每个向量,并从那里将单个向量传递给另一个使用assoc
的函数:
(for [line s] (assemble [line]))
(defn assemble [line]
(let [x (- (read-string (line 1)) 1)]) ; ==> 0 (To match vector of string names)
(assoc line 1 new-name[x]))
但是我将单个向量传递给我的func assemble
时遇到了麻烦。它似乎传递的不是矢量数据,而是传递给它。有没有更好的方法来实现这一目标?
答案 0 :(得分:2)
(def data [[1 1 1 3] [2 2 2 3] [3 2 1 1] [4 3 3 4]])
(def replacements {1 "joe" 2 "fred" 3 "martha"})
user> (mapv (fn [row] (update row 1 replacements)) data)
;;=> [[1 "joe" 1 3] [2 "fred" 2 3] [3 "fred" 1 1] [4 "martha" 3 4]]
在你的情况下,你只是将错误的参数传递给你的assemble
函数:你将它包装在一个你不需要的向量中。
user> (def names ["joe" "fred" "martha"])
#'user/names
user> (defn assemble [line]
(assoc line 1 (names (dec (line 1)))))
#'user/assemble
user> (for [line data] (assemble line))
;;=> ([1 "joe" 1 3] [2 "fred" 2 3] [3 "fred" 1 1] [4 "martha" 3 4])
然后您可以重写assemble
以使用update
:
user> (defn assemble [line]
(update line 1 (comp names dec)))
#'user/assemble
user> (for [line data] (assemble line))
;;=> ([1 "joe" 1 3] [2 "fred" 2 3] [3 "fred" 1 1] [4 "martha" 3 4])
user> (mapv assemble data)
;;=> [[1 "joe" 1 3] [2 "fred" 2 3] [3 "fred" 1 1] [4 "martha" 3 4]]
答案 1 :(得分:1)
您的问题就在于将子向量传递给assemble
函数:
(for [line s] (assemble [line]))
您将子向量line
包装在另一个向量中,以便最终传递[[1 2 3]]
而不是[1 2 3]
。
只需写下
(for [line s] (assemble line))
一切都应该没问题。
编辑:
您应该阅读有关Clojure和Lisps语法的更多信息。您的代码不会在很多方面起作用 例如:
let
创建一个本地绑定。您在其向量中声明的“变量”仅在let
语句中可用。因此,访问x
语句中的assoc
将无效。您要做的是将assoc
来电直接放在let
的结束括号前面,如:(let [x 1] (assoc line 1 x))
new-name[x]
来实现什么,也许可以访问字符串中的字符?这不行。 Lisps中不存在这样的句法诡计。你会使用一个函数。如果我猜对了,请查看clojure.string
命名空间。(line 1)
会给你一个很好的例外,就像“不知道如何使用字符串作为函数”。直接在左括号后面的标识符只能是函数,宏或符号的名称。即使在Clojure中,字符串也是你无法调用的东西。我强烈建议您阅读http://www.braveclojure.com/之类的内容,尤其是前几章。
答案 2 :(得分:-2)
以下是4种方法。方法1& 2使用tupelo.array
函数。
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require [tupelo.array :as tar] ))
(def data
[[1 1 1 3]
[2 2 2 3]
[3 2 1 1]
[4 3 3 4]])
(dotest
(let [arr (tar/rows->array data)
result-1 (-> arr
(tar/elem-set 0 1 "Joe") ; can be in any order here
(tar/elem-set 3 1 "Martha")
(tar/elem-set 2 1 "Fred")
(tar/elem-set 1 1 "Fred"))
row-name-map {0 "Joe"
2 "Fred"
3 "Martha"
1 "Fred"}
result-2 (reduce
(fn [cum-arr [row-idx name]]
(tar/elem-set cum-arr row-idx 1 name))
arr
row-name-map)
方法2b使用原生clojure assoc-in
,将嵌套向量视为关联数据结构(如地图)。
result-2b (reduce
(fn [cum-arr [row-idx name]]
(assoc-in cum-arr [row-idx 1] name))
arr
row-name-map)
方法3使用可变的Java原生数组。
jarr-3 (to-array-2d arr) ; native java, mutable array
>> (doseq [[row-idx name] row-name-map] ; imperative code
(aset jarr-3 row-idx 1 name))
result-3 (mapv vec jarr-3) ] ; convert back to nested clojure vecs
所有4种技术产生相同的结果:
(is= result-1 result-2 result-2b result-3
[[1 "Joe" 1 3]
[2 "Fred" 2 3]
[3 "Fred" 1 1]
[4 "Martha" 3 4]])))