if you supply nth and to map over a vector of vectors, what happens?
user> (map nth [[1 2 3] [4 5 6]])
Error printing return value (ArityException) at clojure.lang.AFn/throwArity (AFn.java:429).
Wrong number of args (1) passed to: clojure.core/nth
Hmm. An error, an “arity exception” and a message that the wrong number of args (1) were passed to nth. How many args does nth take?
user> (use 'clojure.repl)
nil
user> (source nth)
(defn nth
"Returns the value at the index. get returns nil if index out of
bounds, nth throws an exception unless not-found is supplied. nth
also works for strings, Java arrays, regex Matchers and Lists, and,
in O(n) time, for sequences."
{:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~@nf)))
:inline-arities #{2 3}
:added "1.0"}
([coll index] (. clojure.lang.RT (nth coll index)))
([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
nil
Looks like it has two arities: one that takes 2 args - the collection and the index, and another that takes 3 args, the collection, the index, and a result to return if the index does not exist in the collection.
Use your internal evaluator to understand what the expression
(map nth [[1 2 3] [4 5 6]])
ends up doing then, assuming the following definition of map (close enough):
(defn map [f coll] (for [x coll] (f x))) ;; (map inc [1 2 3]) ==> (2 3 4)
(def table [[1 2 3] [4 5 6]])
(map nth table) ;;==> (for [x table] (nth x))
What you wrote can never work because nth
requires more arguments. map
expects a function of 1 argument to apply to each element of the sequence. nth
requires at least 2. It is invoked with (effectively) (nth x)
. Thus an error.
This seems to work:
user> (map (fn [row] (nth row 0)) [[1 2 3] [4 5 6]])
(1 4)
If we look at it through the simplistic definition of map I provided then we get:
(for [row [[1 2 3] [4 5 6]]]
(nth row 0))
;;conceptually equivalent - we aren't actually invoking list via for -, but we get a similar result and this is useful to understand the higher order function:
;;(list (nth [1 2 3] 0) (nth [4 5 6] 0))
;;evaluate and substitute
;;(list 1 (nth [4 5 6] 0))
;;continue
;;(list 1 4)
;;result
;;'(1 4)
As @jgomo3 pointed out, we can supply a function that fills in some of the information for us and meets the expectation of map (a function of a single argument). Since each element of the sequence here is a vector, the argument supplied to the function we send to map is conceptually a row in the table (encoded as a vector). That is the “coll” argument for nth
. So we supply it in a naive fashion to (nth row 0)
.
It is said that getting every kth entry from the rows in a row-major table corresponds to the values of the kth column in the table. In this example, we just got the entries of 0th column of the table [[1 2 3] [4 5 6]] which is '(1 4), by mapping the function (fn [row] (nth row 0)) over the rows of the table, which since the table is a vector of rows, we already have a sequence of rows to pass to map.
If we can extract the 0th column by mapping (fn [row] (nth row 0)), then it stands to reason we can extract the column at various indices:
(fn [row] (nth row 0))
(fn [row] (nth row 1))
(fn [row] (nth row 2))
(fn [row] (nth row 3))
....
(fn [row] (nth row k)) ;;kth column
As @jgomo3 pointed out, we have a problem though. We would like to generalize the concept of extracting “a” column so that the information for the “n” argument to the application of nth
we are using in our function here is itself an argument. How can we generalize this so that k is itself an argument somewhere?
(fn [row] (nth row k)) ;;kth column