Clojure compare sequence to vector - vector

http://www.4clojure.com/problem/23: "Write a function which reverses a sequence"
One solution is (fn [x] (reduce conj () x)), which passes all the tests. However, I'm curious why that solution works for the first test:
(= (__ [1 2 3 4 5]) [5 4 3 2 1])
Inlining the function evaluates to true in the REPL:
(= ((fn [x] (reduce conj () x)) [1 2 3 4 5]) [5 4 3 2 1])
true
However, if I evaluate the first argument to the =, I get (5 4 3 2 1), and (= (5 4 3 2 1) [5 4 3 2 1]) throws a ClassCastException.
Why does the former work while the latter doesn't? It seems like they should be equivalent …

The problem is that your list literal (5 4 3 2 1) is being evaluated as a function call. To use it properly you need to quote it, like so:
(= '(5 4 3 2 1) [5 4 3 2 1]) ;; => true

another way to do it without reduce is to use into ()
as it works exatcly as your reduction. So when you fill the blank this way, it solves the task:
(= (into () [1 2 3 4 5]) [5 4 3 2 1]) ;; true
(= (into () (sorted-set 5 7 2 7)) '(7 5 2)) ;; true
(= (into () [[1 2][3 4][5 6]]) [[5 6][3 4][1 2]]) ;; true

Related

Clojure reduce function for vector containing mixture vectors and doubles

I would like sum a vector containing a mixture of doubles and vectors. Something like,
[[1 2 1 [1 2 3]] [1 2 4 [1 1 1]]...]
That I would like to sum such that I get something like,
[212 12 444 [11 2 12]]
Is there an efficient way to do this using core clojure functions like reduce or map?
I think that you want to have a function that adds a nested vector structure, so that (nvadd [1 2 1 [1 2 3]] [1 2 4 [1 1 1]]) evaluates to [2 4 5 [2 3 4]]. You can then reduce your vector of such vectors with that function.
(defn nvadd [a b]
(mapv #(if (vector? %1)
(nvadd %1 %2)
(+ %1 %2))
a b))
This assumes well-formed input.

Checking if the result of a map contains a false value

When I'm comparing, for example, the following line (These numbers are sample data and can vary).
(map < '[1 2 3 4 5 6] [4 2 3 4 5 9])
I want be able to check if the output contains a false boolean. If it does I want to do some logic, otherwise I want to do some different logic.
This is what I have so far:
(if(map < '[1 2 3 4 5 6] [4 2 3 4 5 9])
// True logic
// False logic)
Thus far it always goes to the true line and never the false line with what ever data I insert. Is there a way I can acheive this?
You could use some with the predicate false?:
(some false? (map < '[1 2 3 4 5 6] [4 2 3 4 5 9])) ;;=> true
(if (some false? (map < '[1 2 3 4 5 6] [4 2 3 4 5 9]))
'True
'False) ;;=> True

common lisp maplist with append output

In the Hyperspec they have this example for maplist:
(maplist #'append '(1 2 3 4) '(1 2) '(1 2 3))
=> ((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3))
which I don't quite grasp. Following the apparent logic, I would have guessed
=>((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3) (3 4 3) (4))
It seems that because we've "exhausted" the middle list after producing the two sublists, it's game over. Why? What am I missing?
Your assertion that it stops after the middle list is "exhausted" seems to be correct; from the documentation:
maplist is like mapcar except that function is applied to successive sublists of the lists. function is first applied to the lists themselves, and then to the cdr of each list, and then to the cdr of the cdr of each list, and so on.
So it makes sense that it would stop after hitting the end of the shortest list, because there would be no more values to use from that list and it seems to try to use all the (sub)lists each time 'round.
EDIT
As Samuel Edwin Ward pointed out:
The part describing mapcar includes "The iteration terminates when the shortest list runs out, and excess elements in other lists are ignored."
Just a quick REPL experiment:
[1]> (maplist #'append '(1 2 3 4) '(1 2) '(1 2 3))
((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3))
[2]> (maplist #'append '(1 2 3 4) '(1 2 3) '(1 2))
((1 2 3 4 1 2 3 1 2) (2 3 4 2 3 2))
[3]> (maplist #'append '(1 2 3 4) '(1 2 3) '(1 2 5))
((1 2 3 4 1 2 3 1 2 5) (2 3 4 2 3 2 5) (3 4 3 5))

Converting inputs of arbitrary depth to vectors

Take a function which inputs a set, set of sets, collection, nested vector with collections buried within, etc. So, the inputs sometimes are not of the same type. How would one go about converting such input into a nested vector structure? In the first case of just a set, I would use
(into [] #{1 2 3 4})
converting the into into a vector.
But the case of sets within sets, I am unable to output nested vectors, i.e.
#{1 2 3 #{1 2 3}}
Similarly, I might have an input such as
[1 2 3 (4 5 6)]
and I want to output
[1 2 3 [4 5 6]]
The idea is that sometimes I need to go within the depth and pick out a collection or set to turn into a vector. Is it possible to have a function, which in general can handle the many different structural inputs and output a nested vector structure. Namely can a function generalize the aforementioned examples? I simplified the samples somewhat, for instance I might have inputs such as
[[[1 2 3 4] [#{1 2 3 4} 2 3 4] [(1 2 3 4) 2 3 4]]]
To give more insight into the function I am trying to work on consider the function C from the language R. The importance of this function lies in the importance of vector structures within statistics/data analysis.
user=> (use 'clojure.walk)
user=> (clojure.walk/postwalk (fn [e] (if (coll? e) (vec e) e)) '(1 2 3 #{4 5 6 (7 8 9)}))
[1 2 3 [4 5 6 [7 8 9]]]
a naive reduce implementation:
(defn to-vectors [c]
(reduce #(conj %1 (if (coll? %2) (to-vectors %2) %2))
[] c))
user=> (to-vectors '[[[1 2 3 4] [#{1 2 3 4} 2 3 4] [(1 2 3 4) 2 3 4]]])
[[[1 2 3 4] [[1 2 3 4] 2 3 4] [[1 2 3 4] 2 3 4]]]

Lisp: multidimensional array elementwise operations

What is the "correct" construct in Common Lisp to apply elementwise operations to multidimensional arrays?
The following examples should help illustrate what I'm trying to do:
A) Suppose I want to increase every element of an array by one:
0 1 2 1 2 3
3 4 5 -> 4 5 6
6 7 8 7 8 9
B) Suppose I want to add 2 arrays:
1 2 -1 -1 0 1
3 4 + -2 -2 -> 1 2
5 6 -3 -3 2 3
C) Suppose I want to find the largest elements of several arrays, elementwise:
max( 0 1 , 4 -1 , 0 0 ) -> 4 1
2 3 0 0 8 1 8 3
Basically I think I'm looking for some sort of "arraymap" function which would be used in like so: (arraymap f A1 A2 ... An), where f takes n arguments as input, and the Ai are arrays of the same size.
In the above examples it would be used like so:
A)
(setq M #2A((0 1 2) (3 4 5) (6 7 8)))
(arraymap #'incf M)
B)
(setq M #2A((1 2) (3 4) (5 6)))
(setq N #2A((-1 -1) (-2 -2) (-3 -3)))
(arraymap #'+ M N)
C)
(setq M #2A((0 1) (2 3)))
(setq N #2A((4 -1) (0 0)))
(setq O #2A((0 0) (8 1)))
(arraymap #'max M N O)
I have tried some constructs with map and loop, but it seems to not work since multidimensional arrays are not a sequence type.
There are four ways to do that:
Write an ARRAY-MAP function based on the array dimensions and iterate over those.
Use ROW-MAJOR-AREF, which views the array like a vector.
Use displaced one-dimensional arrays for the operations.
Example for a use of displaced arrays:
(defun array-map (function &rest arrays)
"maps the function over the arrays.
Assumes that all arrays are of the same dimensions.
Returns a new result array of the same dimension."
(flet ((make-displaced-array (array)
(make-array (reduce #'* (array-dimensions array))
:displaced-to array)))
(let* ((displaced-arrays (mapcar #'make-displaced-array arrays))
(result-array (make-array (array-dimensions (first arrays))))
(displaced-result-array (make-displaced-array result-array)))
(declare (dynamic-extent displaced-arrays displaced-result-array))
(apply #'map-into displaced-result-array function displaced-arrays)
result-array)))
Using it:
CL-USER 3 > (array-map #'1+ #2A((0 1 2) (3 4 5) (6 7 8)))
#2A((1 2 3) (4 5 6) (7 8 9))
CL-USER 4 > (array-map #'+ #2A((1 2) (3 4) (5 6)) #2A((-1 -1) (-2 -2) (-3 -3)) )
#2A((0 1) (1 2) (2 3))
Use internal, implementation specific, operations for efficient array operations.
For anyone coming here looking for an up-to-date answer to this question: https://github.com/bendudson/array-operations defines aops:each (and aops:each*) that does exactly what the OP asks for.

Resources