Map and Nth functions

Working on a homework assignment for a CS program I’m in and running into some issues (see below)

(Useful: map, nth) Write a function, (column table n), where n < (count (first table)) is a number and table is a non-empty vector of vectors. All nested vectors in table will have the same non-zero size.
That is, table is a “matrix.” Return a vector or list containing the elements of the n-th column (zero-based
indexing) of table. Sample runs: Homework2-Clojure.pdf 2022-...

Anyone know how I can accomplish the above using the map and nth functions? What I’ve been trying (map (nth table n))) doesn’t work and I’m sort of stuck.

If you can get the nth entry from a collection

(nth coll n)

and a table is a collection of collections (vector of vectors),
it’s reasonable to assume we can get the ith row

(nth table i)

and the jth entry from said row

(nth (nth table i) j)

[as an side, we can also look at it like this since vectors are functions in clojure (akin to nth):]

((table i) j)

the non-transducer version of map (the more common “map” we think of) takes 2 arguments: a function and a sequence to “map” over, where we construct a new sequence that corresponds to (for the simplest case of a function of 1 arg) the following:

(defn map [f xs] (for [x xs] (f x)))

So,

(map inc [1 2 3 4]) ;;(2 3 4 5)

We can view all the rows of the table using map if the table is encoded in row-major form:

(def table [[1 2 3 4] [5 6 7 8]])
(map identity table) ;;([1 2 3 4] [5 6 7 8])

If we have a way to pick out the nth entry from every row using map, then it seems the resulting sequence would denote a column.

Knowing how map works, and how nth works, what would we supply as the function f “to map” over the sequence (the nested vector) defined by the row-major table, to get the nth entry?

So would nth not be that function you supply to map over the vector of vectors? I was under the impression it would be?

Sorry, it’s week two into this course and I’ve never used, heard of, seen clojure before it started :rofl:

You are close. It will be something like nth but not exactly that.

Depending on n, it will be a function that returns the nth element (0-indexed) of an array.

What I mean is that you can’t know what function to use until you are evaluating the column function for a given n.

If n is 1, you have to pass a function, let’s call it F1, to map.
But if n is 2, you have to pass to map, another function, let’s call it F2.

So, how to produce function dynamically?

You can create a function (let’s call it F) that, given n returns a 1-argument function (Fn) that get’s the n-th element (0-indexed) of it’s argument.

So, (F1 [0 1 2]) is 1, and (F2 [0 1 2]) is 2.

F is a function that produce functions. Let me help you here, I will tell you how to create that F function:

(defn F [n] (fn [vector] (nth vector n)))

This F function you can use to produce F1 or F2 for example:

(F 1) ;; This returns a function behaving as I described F1
(F 2) ;; This returns a function behaving as I described F2

((F 1) [0 1 2]) ;; 1
((F 2) [0 1 2]) ;; 2

Let’s be clear that (map (nth table n)) does not supply nth as the function to map, but the result of (nth table n). If you wanted to supply nth as the function it would look like (map nth).

What you want, methinks, is not to supply nth to map, but a function that uses nth.

Consider the following, if table is defined (def table [[1 2 3] [4 5 6] [7 8 9]]), and n is defined (def n 1):

(map identity table) returns ([1 2 3] [4 5 6] [7 8 9]). Here we are supplying the identity function to map.

(map (fn [row] row) table) returns the same. Here we are supplying the function returned by (fn [row] row) to map.

(map (fn [row] (nth row n)) table) returns (2 5 8), ie the nth column of table if n is 1.

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

Got it working, thanks for the tips all!

1 Like

if you copied that code verbatim, that is not the solution and you will get an F :slight_smile: I used for in some of the examples because it’s not using map, which the assignment requires, yet you end up in the same place. What is your solution using map and nth?

I figured this out last night before you posted this. The function takes n as an argument, and calls another function that takes a vector as an argument. Inside of that vector function it calls (nth vector n), which gives me the correct output. I then call (map (f n) table)