I just got started with Clojure on Heroku, first reading this introduction.
Now in the phase of getting my hands dirty, I am facing this issue handling a database in a loop.
This is working:
(for
[s (db/query (env :database-url)
["select * from My_List"])]
; here one can do something with s, for example:
; print out (:field s)
)
But it is not enough to update variables inside the loop as I want.
Reading on the subject, I understand that Clojure having its own way of handling variables I need to use a loop pattern.
Here is what I tried:
(loop [a 0 b 1
s (db/query (env :database-url)
["select * from My_List"])]
; here I want to do something with s, for example
; print out (:field s)
; and do the following ... but it does not work!
(if (> (:otherField s) 5)
(:otherField s)
(recur (+ a (:otherField s)) b s))
)
Since I tried various ways of doing before writing this post, I know that the code above works except for the fact that I am doing something wrong concerning the database.
So here comes my question: What do I need to change to make it work?
I see, that it's hard to get to the functional thinking at first, when you're used to a different paradigm.
I don't think there is a correct explanation on “how to do this loop right”, because it's not right to do a loop here.
The two things that feel most incorrect to me:
Never do a SELECT * FROM table. This is not how relational databases are ment to be used. For example when you want the sum of all values greater than 5 you should do: SELECT SUM(field) FROM my_list WHERE field > 5
Don't think in loops (how to do it) but in what you want to do with the data:
I want to work on field :otherFIeld
I am only interested in values bigger than 5
I want the sum of all the remaining values
Then you come to something like this:
(reduce +
(filter #(> % 5)
(map :otherField
(db/query (env :database-url) ["select * from My_List"]))))
(No loop at all.)
Related
Wonder if there's even a way to vectorize this... Let's say I have the following arrays of the same length, A, B, C. I have in pseudocode:
> for i in range(2, length(A)):
> if (A(i) > B(i)) && (C(i) > C(i - 1)):
> C(i) = 1
> else:
> C(i) = 0
Here I'm using the previous value of C in my logic, but the values of C depend on the logic itself. Is there a way to re-write this so it is vectorized? So I'm comparing entire vectors, as opposed to doing for loops? If not, is there a way to do a two part calculation, so that the maybe only part of it is element wise, and the rest is vectorized? I have a huge if statement with a lot of conditions (40+ conditions) - and am trying to simplify it but can't think of a way due to the fact that it depends on yesterday's value...
Thanks!
Ultimately, I want to change scores of 0 to 1, scores of 1 to 2, and scores of 2 to 3. I thought one way to do that was using +1, but I realize I could also use a more complicated if then series.
Here is what I did so far:
I used the existing variable (x) to create a new variable (y=x+1) using SPSS syntax. I only want to do this for variables with values >=0 (this was my approach to excluding cells with missing data; the range for x is 0-2).
I can create x+1, but it overwrites the existing variables.
DO REPEAT x =var_1 TO var_86.
if (x>=0) x=(x+1).
end repeat.
exe.
I tried this modification, but it doesn't work:
DO REPEAT x = var_1 TO var_86 / y = var_1a TO var_86a.
IF (x >= 0) y=x +1.
END REPEAT.
EXE.
The error message is:
DO REPEAT The form VARX TO VARY to refer to a range of variables has
been used incorrectly. When using VARX TO VARY to create new
variables, X must be an integer less than or equal to the integer Y.
(Can't use A3 TO A1.)
I tried many other configurations including vectors and loops but haven't yet figured out how to do this computation across the range of variables without overwriting the existing ones. Thanks in advance for any recommendations.
The message you are getting is because SPSS doesn't understand the form var_1a TO var_86a.
For the x to y form to work the number has to be at the end of the name, so for example varA_1 to varA_86 should work.
While you're at it, here's a simple way to go about your task:
recode var_1 TO var_86 (0=1)(1=2)(2=3) into varA_1 TO varA_86.
Preface
Firstly, I'm new to Clojure and programming, so I thought I'd try to create a function that solves a non-trivial equation using my natural instincts. What resulted is the desire to find a square root.
The question
What's the most efficient way to stop my square-n-map-maker function from iterating past a certain point? I'd like to fix square-n-map-maker so that I can comment out the square-maker function which provides me with the results and format I currently want to see but not the ability to recall the square-root answer (insofar as I know).
I.e. I want it to stop when it is greater than or equal to my input value
My initial thought was that instead of a keyword list, I would want it to be a map. But I'm having a very difficult time getting my function to give me a map. The whole reason I wanted a map where one member of a pair is n and another is n^2 so that I could extract the actual square root from it and it give it back to the user as the answer.
Any ideas on the best way to accomplish this? (below is the function I want to fix)
;; attempting to make a map so that I can comb over the
;; map later and recall a value that meets
;; my criteria to terminate and return result if (<= temp-var input)
(defn square-n-map-maker [input] (for [temp-var {remainder-culler input}]
(map list(temp-var) (* temp-var temp-var))
)
)
(square-n-map-maker 100) => clojure.lang.ArityException: Wrong number of args (0) passed to: MapEntry
AFn.java:437 clojure.lang.AFn.throwArity
AFn.java:35 clojure.lang.AFn.invoke
/Users/dbennett/Dropbox/Clojure Files/SquareRoot.clj:40 sqrt-range-high-end/square-n-map-maker[fn]
The following is the rest of my code
;; My idea on the best way to find a square root is simple.
;; If I want to find the square root of n, divide n in half
;; Then find all numbers in 0...n that return only a remainder of 0.
;; Then find the number that can divide by itself with a result of 1.
;; First I'll develop a function that works with evens and then odds
(defn sqrt-range-high-end [input] (/ input 2))
(sqrt-range-high-end 100) => 50
(defn make-sqrt-range [input] (range (sqrt-range-high-end (+ 1 input))))
(make-sqrt-range 100) =>(0 1 2 3 4 5 6 ... 50)
(defn zero-culler [input] (remove zero? (make-sqrt-range input)))
(zero-culler 100) =>(1 2 3 4 5 6 ... 50)
(defn odd-culler [input] (remove odd? (zero-culler input)))
(odd-culler 100) => (2 4 6 8 10...50)
(defn even-culler [input] (remove even? (zero-culler input)))
(even-culler 100) => (1 3 5 7...49)
(defn remainder-culler [input] (filter #(zero? (rem input %)) (odd-culler input)))
(remainder-culler 100) => (2 4 6 12 18)
(defn square-maker [input] (for [temp-var (remainder-culler input)]
(list (keyword (str
temp-var" "
(* temp-var temp-var)
)
)
)
)
(square-maker 100) => ((:2 4) (:4 16) (:10 100) (:20 400) (:50 2500))
Read the Error Messages!
You're getting a little ahead of yourself! Your bug has nothing to do with getting for to stop "looping."
(defn square-n-map-maker [input] (for [temp-var {remainder-culler input}]
(map list(temp-var) (* temp-var temp-var))))
(square-n-map-maker 100) => clojure.lang.ArityException: Wrong number of args (0) passed to: MapEntry
AFn.java:437 clojure.lang.AFn.throwArity
AFn.java:35 clojure.lang.AFn.invoke
Pay attention to error messages. They are your friend. In this case, it's telling you that you are passing the wrong number of arguments to MapEntry (search for IPersistentMap). What is that?
{} creates a map literal. {:key :value :key2 :value2} is a map. Maps can be used as if they were functions:
> ({:key :value} :key)
:value
That accesses the entry in the map associated with key. Now, you created a map in your first line: {remainder-culler input}. You just mapped the function remainder-culler to the input. If you grab an item out of the map, it's a MapEntry. Every MapEntry can be used as a function, accepting an index as an argument, just like a Vector:
> ([:a :b :c :d] 2)
:c
Your for is iterating over all MapEntries in {remainder-culler input}, but there's only one: [remainder-culler input]. This MapEntry gets assigned to temp-var.
Then in the next line, you wrapped this map in parentheses: (temp-var). This forms an S-expression, and expressions are evaluated assuming that the first item in the expression is a function/procedure. So it expects an index (valid indices here would be 0 and 1). But you pass no arguments to temp-var. Therefore: clojure.lang.ArityException: Wrong number of args.
Also, note that map is not a constructor for a Map.
Constructing a map
Now, on to your problem. Your square-maker is returning a list nicely formatted for a map, but it's made up of nested lists.
Try this:
(apply hash-map (flatten (square-maker 100)))
Read this page and this page to see how it works.
If you don't mind switching the order of the keys and values, you can use the group-by that I mentioned before:
(defn square-maker [input]
(group-by #(* % %) (remainder-culler input)))
(square-maker 100) => {4 [2], 16 [4], 100 [10], 400 [20], 2500 [50]}
Then you can snag the value you need like so: (first ((square-maker 100) 100)). This uses the map-as-function feature I mentioned above.
Loops
If you really want to stick with the intuitive looping concept, I would use loop, not for. for is lazy, which means that there is neither means nor reason (if you use it correctly) to "stop" it -- it doesn't actually do any work unless you ask for a value from it, and it only does the work it must to give you the value you asked for.
(defn square-root [input]
(let [candidates (remainder-culler input)]
(loop [i 0]
(if (= input (#(* % %) (nth candidates i)))
(nth candidates i)
(recur (inc i))))))
The embedded if determines when the looping will cease.
But notice that loop only returns its final value (acquaint yourself with loop's documentation if that sentence doesn't make sense to you). If you want to build up a hash-map for later analysis, you'd have to do something like (loop [i 0, mymap {}] .... But why analyze later if it can be done right away? :-)
Now, that's a pretty fragile square-root function, and it wouldn't be too hard to get it caught in an infinite loop (feed it 101). I leave it as an exercise to you to fix it (this is all an academic exercise anyway, right?).
I hope that helps you along your way, once again. I think this is a great problem for learning a new language. I should say, for the record, though, that once you are feeling comfortable with your solution, you should search for other Clojure solutions to the problem and see if you can understand how they work -- this one may be "intuitive," but it is not well-suited to Clojure's tools and capabilities. Looking at other solutions will help you grasp Clojure's world a bit better.
For more reading:
Imperative looping with side-effects.
How to position recur with loop
The handy into
Finally, this "not constructive" list of common Clojure mistakes
for is not a loop, and it's not iterating. It lazily creates a list comprehension, and it only realizes values when required (in this case, when the repl tries to print the result of the evaluation). There are two usual ways to do what you want: one is to wrap square-maker in
(first (filter some-predicate (square-maker number))) to obtain the first element in the sequence that complies with some-predicate. E.g.
(first (filter #(and (odd? %) (< 50 %)) (range)))
=> 51
The above won't realize the infinite range, obviously.
The other one is not to use a list comprehension and do it in a more imperative way: run an actual loop with a termination condition (see loop and recur).
Example:
(loop [x 0]
(if (and (odd? x) (> x 50))
x
(recur (inc x))))
I am using a program (TracePro) that uses Scheme, which I haven't used all that much. I want to create a code that changes some initial values, runs a simulation, and then saves the resulting data table to a file, then changes the values again, runs the simulation, saves data, etc. for 90 times. The code I've created so far:
(raytrace:set-beam-orientation-euler-degrees (gvector 0 90 -90))
(raytrace:grid)
(edit:select (car (cdr (entity:faces (entity 12)))))
(analysis:incident)
(analysis:incident-save "C:/Users/Admin/Desktop/testdata/incident0.csv" "csv")
Is there a way to create a loop that would run this piece of code with different incident angle, from 0 to 90, and different file name, from incident0.csv all the way to incident90.csv, without having to copy the code 90 times, and change it manually...? I sort of have an idea how to handle the changing incident angle, but no idea about the file name change.
Thanks in advance.
I don't know the specifics of TracePro, but in any Scheme program you can loop by using recursion. First, we must refactor the parts in the code that change and make them parameters, encapsulating the code in a function. I'm not sure which value is supposed to be the incident angle, please adjust as necessary:
(define (run-simulation angle)
(raytrace:set-beam-orientation-euler-degrees
(gvector angle 90 -90)) ; assuming that the first parameter is the angle
(raytrace:grid)
(edit:select
(car (cdr (entity:faces (entity 12)))))
(analysis:incident)
(analysis:incident-save
(string-append ; this is how we can dynamically create file names
"C:/Users/Admin/Desktop/testdata/incident"
(number->string angle)
".csv")
"csv"))
With the above procedure in place, it's easy to loop on it, calling it as many times as needed. Notice how we use the base case of the recursion for stopping, and how at the recursive step we increment the current value:
(define (loop init end)
(cond ((> init end) 'done) ; base case of recursion
(else ; otherwise
(run-simulation init) ; call the previous procedure
(loop (+ init 1) end)))) ; advance recursion
Now we just have to provide appropriate start and stop parameters at the time we call the loop:
(loop 0 90)
If we're smart about it, we can reuse the same looping procedure for other purposes, notice that the only thing that might change is the procedure that gets called - so we can pass it as a parameter, too!
(define (loop func init end)
(cond ((> init end) 'done)
(else
(func init)
(loop func (+ init 1) end))))
(loop run-simulation 0 90)
There are other tricks you can apply to make the program more flexible, but for the time being, the above code should put you on the right track. Happy Scheming!
Ok. That's my problem. I need to implement a predicate that sums up all the prices of the products in the list. But, for now, I'm not getting any further with it.
What am I doing wrong?
Thanks in advance.
domains
state = reduced ; normal
database
producte (string, integer, state)
predicates
nondeterm calculate(integer)
clauses
% ---> producte( description , price , state )
producte("Enciam",2,normal).
producte("Llet",1,reduced).
producte("Formatge",5,normal).
calculate(Import):-
producte(_,Import,_).
calculate(Import):-
producte(_,Import,_),
calculate(Import2),
Import=Import2+Import,!.
Goal
calculate(I).
Disclaimer: I'm a bit daft when it comes to prolog. Also, I don't have access to a prolog interpreter right now.
The canonical example, sum of a list:
sum([], 0).
sum([Head | Tail], Total) :- sum(Tail, Temp), Total is Head + Temp.
making a list with findall/3:
findall(Val, producte(_, Val, _), Vals).
Vals has your list you want to sum.
Update: per your comment, I'm a bit out of my depth without access to an interpreter.
calculate(I) :- sum(Vals, I), findall(Val, producte(_, Val, _), Vals).
What I think this does:
uses your single goal I, which receives the result of summing your Vals list, which is generated by findall. But it's been so long since I've used prolog that I may not even have the syntax right for doing what I want. However, a small variation should accomplish what you want with a single goal.
The findall part :
calculate(Price) :-
List = [ Price || producte(_, Price, _) ],
sum_list(List, 0, Sum).
The sum_list part :
sum_list([], Acc, Acc).
sum_list([Head|Tail], Acc, Sum) :-
NewAcc is Acc + Head,
sum_list(Tail, NewAcc, Sum).
I guess something along these lines should work according to visual-prolog doc but I kinda don't wanna install visual-prolog to test it...