连接任意输入的函数

sun*_*ots 2 clojure clojure-contrib

我想编写一个连接向量或矩阵的函数,它可以接受任意输入.为了组合两个向量,我编写了以下代码.它还可以组合矩阵,以便延长列.

(defn concats 
([x y] (vec (concat x y))))
Run Code Online (Sandbox Code Playgroud)

我被卡住的地方是将输入扩展到n个向量或矩阵,并组合矩阵以产生更长的行.

例)(某些函数[[:a:b] [:c:d]] [[1 2] [3 4]] 2]

[[:a :b 1 2] [:c :d 3 4]]
Run Code Online (Sandbox Code Playgroud)

输入中的2指定级别以进行连接.

Omr*_*ein 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)))
Run Code Online (Sandbox Code Playgroud)

它是如何工作的简短的回答是这样的:它反复建立了一个功能,将嵌套调用into*正确的级别,然后将其应用到所提供的矩阵.

into给定向量第一个参数的情况下,常规old 将第二个参数的元素连接到向量的末尾.into*这里的函数就是我在可变数量的向量上进行向量连接的方式.它用于reduce迭代地调用into一些累积的向量(以as开头to)和列表中的连续向量froms.例如:

user> (into* [1 2] [3 4] [5 6])
> [1 2 3 4 5 6]
Run Code Online (Sandbox Code Playgroud)

现在deep-into*,我必须认识到一种模式.我开始通过手工编写不同的表达式来满足不同的级联级别.对于0级,它很容易(我已经稍微推断了你的例子,所以我可以把它变成2级):

user> (into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d]] [[1 2] [3 4]]]
Run Code Online (Sandbox Code Playgroud)

至于第1级,它仍然非常简单.我使用mapv,它的工作方式就像map它返回一个向量而不是一个懒惰的序列:

user> (mapv into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d] [1 2] [3 4]]]
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

所以知道这一点,并且知道我想要更深入一级,这有点意义,第2级看起来像这样:

user> (mapv (partial mapv into*) [[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
Run Code Online (Sandbox Code Playgroud)

在这里,它映射了一个映射into*到某个集合的函数.这有点像说:映射(mapv into* ...)矩阵的1级概念.为了将其概括为函数,您必须在此处识别模式.我要把它们全部放在一起:

(into* ...) ;level 0
(mapv into* ...) ;level 1
(mapv (partial mapv into*) ...) ;level 2
Run Code Online (Sandbox Code Playgroud)

从这里开始,我记得那(partial f)是一样的f(考虑一下:你有一个功能,你没有给它额外的"假设"参数).通过扩展一点,(map f ...)是相同的,((partial map f) ...)所以我将重写以上,稍微:

(into* ...) ;level 0
((partial mapv into*) ...) ;level 1
((partial mapv (partial mapv into*)) ...) ;level 2
Run Code Online (Sandbox Code Playgroud)

现在迭代模式变得越来越清晰.我们呼吁一些功能...(这只是我们给出矩阵),并且函数调用的反复积聚(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
Run Code Online (Sandbox Code Playgroud)

如果没有(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]]]
Run Code Online (Sandbox Code Playgroud)

然后,使用->宏我可以分解出一些嵌套的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]]]
Run Code Online (Sandbox Code Playgroud)

从这里,推广到函数是非常简单的 - 2用参数替换矩阵和矩阵.但是因为这需要可变数量的矩阵,我们将不得不apply使用迭代构建的函数.该apply宏接受一个函数或宏的参数个数可变,最后的集合.本质上,它将函数或宏和提供的参数预先添加到最终列表中,然后评估整个事物.例如:

user> (apply + [1 5 10]) ;same as (+ 1 5 10)
> 16
Run Code Online (Sandbox Code Playgroud)

令人高兴的是,我们可以坚持apply到底(-> ...).为了对称起见,这是我的解决方案:

(defn deep-into*
  [level & matrices]
  (-> (partial partial mapv)
      (iterate into*)
      (nth level)
      (apply matrices)))
Run Code Online (Sandbox Code Playgroud)