2

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

(defn concats 
([x y] (vec (concat x y))))

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

例如)(somefunction [[:a :b] [:c :d]] [[1 2] [3 4]] 2]

[[:a :b 1 2] [:c :d 3 4]]

输入中的 2 指定要连接的级别。

4

3 回答 3

5

如果你对“它是如何工作的”不感兴趣,这里是前面的解决方案(注意它level是零索引的,所以你称之为第 1 级,我称之为第 0 级):

(defn into* [to & froms]
  (reduce into to froms))

(defn deep-into*
  [level & matrices]
  (-> (partial partial mapv)
      (iterate into*)
      (nth level)
      (apply matrices)))

它如何工作的简短答案是:它迭代地构建一个函数,将调用嵌套into*在正确的级别,然后将其应用于提供的矩阵。

给定向量第一个参数的常规 oldinto会将第二个参数的元素连接到向量的末尾。这里的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*向下映射某个集合的函数。这有点像说:将第 1 级的想法映射(mapv into* ...)到矩阵中。为了将其推广到函数,您必须在此处识别模式。我要把它们都放在一起:

(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

现在迭代模式变得更加清晰。我们正在调用一些函数 on ...(这只是我们给定的矩阵),并且该函数是调用(partial mapv ...)on的迭代构建into*,迭代级别数。该(partial mapv ...)部分可以功能化为(partial partial mapv). 这是一个偏函数,它返回mapv一些提供的参数的偏函数。这个外部partial不是很必要,因为我们知道...这里永远是一回事。所以我们可以像写它一样容易#(partial mapv %),但我很少有机会使用(partial partial ...)它,而且我认为它看起来很漂亮。至于迭代,我使用 pattern (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]]]

然后,使用->宏我可以分解出一些嵌套的括号。这个宏接受一个表达式列表,并递归地将每个表达式嵌套到连续表达式的第二个位置。它没有添加任何功能,但肯定可以使内容更具可读性:

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)))
于 2013-11-20T06:31:36.763 回答
0

Here are two different solutions for a function which will return a vector that's the concatenation of an arbitrary number of input collections:

(defn concats [& colls]
  (reduce (fn [result coll]
            (into result coll))
          []
          colls))

(defn concats [& colls]
  (vec (apply concat colls)))

The [& arg-name] notation in the argument lists is how you specify that the function is "variadic" - meaning it can accept a variable number of arguments. The result is that colls (or whatever name you pick) will be a sequence of all the arguments in excess of the positional arguments.

Functions can have multiple arities in Clojure, so you can also do things like this:

(defn concats
  ([x]
     (vec x))
  ([x y]
     (vec (concat x y)))
  ([x y & colls]
     (vec (apply concat (list* x y colls)))))

However, only one of the overloads can be variadic, and its variadic part must come last (i.e. you can't do [& more n], only [n & more].

The Clojure.org page on special forms has more useful information on argument lists in Clojure (in the section on fn).

The function below correctly handles the example input/output you provided. Unfortunately I don't think I understand how you want the levels (and associated numeric input) to work well enough to generalize it as far as you're looking for.

(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]]

But maybe it will give you some ideas anyway, or if you can add more information (especially examples of how different levels should work) I'll see if I can be more help.

于 2013-11-19T22:46:41.590 回答
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"))
于 2013-11-19T22:40:20.600 回答