我想编写一个连接向量或矩阵的函数,它可以接受任意输入。为了组合两个向量,我编写了以下代码。它还可以组合矩阵,以便延长列。
(defn concats
([x y] (vec (concat x y))))
我遇到的问题是将输入扩展为n个向量或矩阵,并组合矩阵以产生更长的行。
Ex)(某些功能[[:a:b] [:c:d]] [[1 2] [3 4]] 2]
[[:a :b 1 2] [:c :d 3 4]]
输入中的2指定要连接的级别。
答案 0 :(得分:5)
如果你对“它是如何工作”不感兴趣,那么这就是前面的解决方案(注意level
是零索引的,所以你称之为第一级我称之为第0级电平):
(defn into* [to & froms]
(reduce into to froms))
(defn deep-into*
[level & matrices]
(-> (partial partial mapv)
(iterate into*)
(nth level)
(apply matrices)))
它的工作原理的简短答案是:它迭代地构建一个函数,将调用into*
嵌套在正确的级别,然后将其应用于提供的矩阵。
给定向量第一个参数的常规旧into
将第二个参数的元素连接到向量的末尾。这里的into*
函数就是我在可变数量的向量上进行向量连接的方式。它使用reduce
在一些累积向量(以into
开头)和列表to
中的连续向量上迭代调用froms
。例如:
user> (into* [1 2] [3 4] [5 6])
> [1 2 3 4 5 6]
现在deep-into*
,我必须认识到一种模式。我开始通过手工编写不同的表达式来满足不同的级联级别。对于0级,这很容易(我已经稍微推断了你的例子,所以我可以把它变成2级):
user> (into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d]] [[1 2] [3 4]]]
至于第1级,它仍然非常简单。我使用mapv
,它的工作方式与map
类似,只是它返回一个向量而不是一个惰性序列:
user> (mapv into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d] [1 2] [3 4]]]
2级更多参与。这是我开始使用partial
的地方。 partial
函数接受一个函数和一个可变数量的参数参数(不是拼写错误),并返回一个“假定”给定参数的新函数。如果有帮助,(partial f x)
与(fn [& args] (apply f x args))
相同。这个例子应该更清楚:
user> ((partial + 2) 5)
> 7
user> (map (partial + 2) [5 6 7]) ;why was six afraid of seven?
> (7 8 9)
所以知道这一点,并且知道我想要更深入一级,因此第2级看起来像是这样:
user> (mapv (partial mapv into*) [[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
这里,它映射了一个将into*
映射到某个集合的函数。这有点像说:将(mapv into* ...)
的1级概念映射到矩阵中。为了将其概括为函数,您必须在此处识别模式。我要把它们全部放在一起:
(into* ...) ;level 0
(mapv into* ...) ;level 1
(mapv (partial mapv into*) ...) ;level 2
从这里开始,我记得(partial f)
与f
相同(想一想:你有一个功能而且你没有给它额外的“假设”参数)。通过稍微扩展一点,(map f ...)
与((partial map f) ...)
相同所以我将稍微重写以上内容:
(into* ...) ;level 0
((partial mapv into*) ...) ;level 1
((partial mapv (partial mapv into*)) ...) ;level 2
现在迭代模式变得越来越清晰。我们在...
(这只是我们给定的矩阵)上调用了一些函数,该函数是在(partial mapv ...)
上调用into*
的迭代构建,迭代了级别数。 (partial mapv ...)
部分可以功能化为(partial partial mapv)
。这是一个部分函数,它返回mapv
一些提供的参数的部分函数。这个外部partial
不是必需的,因为我们知道这里的...
总是一件事。所以我们可以像#(partial mapv %)
一样轻松地编写它,但我很少有机会使用(partial partial ...)
,我觉得它看起来很漂亮。至于迭代,我使用模式(nth (iterate f initial) n)
。也许另一个例子可以使这种模式清晰:
user> (nth (iterate inc 6) 5)
> 11
如果没有(nth ...)
部分,它将永远循环,创建一个大于或等于5的递增整数的无限列表。所以现在,整个事物被抽象并计算为第2级:
user> ((nth (iterate (partial partial mapv) into*) 2)
[[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
然后,使用->
宏我可以分解这些嵌套的parantheses。该宏获取表达式列表,并递归地将每个表达式嵌入到后续表达式的第二个位置。它没有添加任何功能,但肯定可以使事情更具可读性:
user> ((-> (partial partial mapv)
(iterate into*)
(nth 2))
[[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
从这里,推广到函数是非常简单的 - 用参数替换2
和矩阵。但是因为这需要不同数量的矩阵,所以我们必须apply
迭代构建的函数。 apply
宏采用函数或宏,可变数量的参数,最后是集合。本质上,它将函数或宏和提供的参数预先添加到最终列表中,然后评估整个事物。例如:
user> (apply + [1 5 10]) ;same as (+ 1 5 10)
> 16
令人高兴的是,我们可以在apply
的末尾贴上所需的(-> ...)
。为了对称起见,这是我的解决方案:
(defn deep-into*
[level & matrices]
(-> (partial partial mapv)
(iterate into*)
(nth level)
(apply matrices)))
答案 1 :(得分:0)
使用您在问题中列出的concats函数:
user=> (map concats [[:a :b] [:c :d]] [[1 2] [3 4]])
([:a :b 1 2] [:c :d 3 4])
这不会考虑您列出的级别,但会处理给定的输入
任意数量的参数需要替换concats函数
(defn conc [a b & args]
(if (nil? (first args))
(concat a b)
(recur (concat a b) (first args) (rest args))))
以下是两个例子
user=> (map conc [[:a :b] [:c :d]] [[1 2] [3 4]] [["w" "x"] ["y" "z"]])
((:a :b 1 2 "w" "x") (:c :d 3 4 "y" "z"))
user=> (map conc [[:a :b] [:c :d] [:e :f]] [[1 2] [3 4] [5 6]] [["u" "v"] ["w" "x"] ["y" "z"]])
((:a :b 1 2 "u" "v") (:c :d 3 4 "w" "x") (:e :f 5 6 "y" "z"))
答案 2 :(得分:0)
以下是函数的两种不同解决方案,它将返回一个向量,该向量是任意数量的输入集合的串联:
(defn concats [& colls]
(reduce (fn [result coll]
(into result coll))
[]
colls))
(defn concats [& colls]
(vec (apply concat colls)))
参数列表中的[& arg-name]
表示法是指定函数是“variadic”的方式 - 这意味着它可以接受可变数量的参数。结果是colls
(或您选择的任何名称)将是超出位置参数的所有参数的序列。
Clojure中的函数可以有多个arity,所以你也可以这样做:
(defn concats
([x]
(vec x))
([x y]
(vec (concat x y)))
([x y & colls]
(vec (apply concat (list* x y colls)))))
但是,只有一个重载可以是可变参数,并且它的可变参数部分必须是最后一个(即你不能[& more n]
,只能[n & more]
。
Clojure.org page on special forms在Clojure中的参数列表中有更多有用的信息(在fn
部分)。
以下功能正确处理您提供的示例输入/输出。不幸的是,我不认为我理解你希望水平(和相关的数字输入)如何运作得足以将其概括为你想要的。
(defn concats [x y]
;; only works with two inputs
(vec (map-indexed (fn [i v] (into v (nth y i)))
x)))
(concats [[:a :b] [:c :d]] [[1 2] [3 4]]) ;=> [[:a :b 1 2] [:c :d 3 4]]
但也许它会给你一些想法,或者如果你可以添加更多信息(特别是不同级别应该如何工作的例子),我会看看我是否可以提供更多帮助。