Trying to set finalResult to equal only the array values that contain "chocolate" Clojure - filter

;;the only function the works is cartesian-product
(def flavorsOne ["chocolate" "Vanilla" "Cherry Ripple"])
(def flavorsTwo ["Lemon" "Butterscotch" "Licorice Ripple"])
;; (def result (map vector flavorsOne flavorsTwo))
(defn cartesian-product
([] '(()))
([xs & more]
(mapcat #(map (partial cons %)
(apply cartesian-product more))
xs)))
(def resultTwo (cartesian-product flavorsOne flavorsTwo))
(def finalResult (filter ["chocolate"] resultTwo))
(filter #(= (:name %) "choccolate") resultTwo)
;; (filter #(= (:name %) "chocolate") flavorsTwo)
;; (println flavorsOne flavorsTwo)
;; (defn IceCreamStore
;; ())

I'll try to understand, but please put more efforts in elaborating your problem next time you post.
(def flavors-1 ["chocolate" "Vanilla" "Cherry Ripple"])
(def flavors-2 ["Lemon" "Butterscotch" "Licorice Ripple"])
(->> (for [x flavors-1
y flavors-2]
#{x y})
(filter #(contains? % "chocolate")))
I think this is what you'd be looking for.
Your cartesian-product function is overly complicated, and can be succinctly done using for(https://clojuredocs.org/clojure.core/for).
When using 'for', it's better to have the result as sets, because you want to know whether an element (chocolate) is inside or not. If you want the for part to be a function,
(defn cartesian-product [x y]
(for [x flavors-1
y flavors-2]
#{x y}))
Minor thing. in Clojure, it is typical to use hyphens(-) rather than camel cases.

Related

How do non-binary foldl and foldr work in Racket?

I'm familiar with the underlying workings and differences of foldl and foldr over a single list. However, in Racket, you can use folds on multiple lists. For example, you can find the difference of elements in two lists by writing
; (mapMinus '(3 4) '(1 2)) => '(2 2)
(define (mapMinus lst0 lst1)
(foldl (λ (hd0 hd1 acc) (cons (- hd0 hd1) acc)) '() lst0 lst1))
How exactly do Racket's implementations of foldl and foldr work to handle multiple lists? The Racket source code for foldl is available on GitHub here, but I don't know Chez Scheme well enough to understand it.
A fold that operates over multiple lists simply applies its lambda element-wise on all of the lists, simultaneously. Perhaps a simplified implementation (with no error checking, etc.) of it will make things clearer; let's compare a standard implementation of foldr (which IMHO is slightly simpler to understand than foldl):
(define (foldr proc init lst)
(if (null? lst)
init
(proc (car lst)
(foldr proc init (cdr lst)))))
With an implementation that accepts multiple lists:
(define (foldr proc init . lst) ; lst is a list of lists
(if (null? (car lst)) ; all lists assumed to be of same length
init
; use apply because proc can have any number of args
(apply proc
; append, list are required for building the parameter list
; in the right way so it can be passed to (apply proc ...)
(append (map car lst)
; use apply again because it's a variadic procedure
(list (apply foldr proc init (map cdr lst)))))))
All the extra code in the multi-list version is for applying proc to multiple elements at the same time, getting the current element of each list (map car lst) and advancing over all the lists (map cdr lst).
Also the implementation needs to take into account that the procedure operates over a variable number of lists, assuming that the provided lambda receives the correct number of arguments (number of input lists + 1). It works as expected:
(foldr (lambda (e1 e2 acc)
(cons (list e1 e2) acc))
'()
'(1 2 3)
'(4 5 6))
=> '((1 4) (2 5) (3 6))
I think what you really are asking is how to create a variadic function in Scheme/Racket. The answer is given at https://docs.racket-lang.org/guide/define.html, but let me just give you a quick example:
(define (foo a b . xs)
(+ a b (length xs)))
would be equivalent to
def foo(a, b, *xs):
return a + b + len(xs)
in Python. xs here is a list value containing the rest of the arguments.
The second piece of puzzle is, how to apply a variadic function with a list value. For that, you can use apply. Read more at https://docs.racket-lang.org/guide/application.html#%28part._apply%29. Again, here's a quick example:
(define (foo a b c) (+ a b c))
(apply foo 1 '(2 3))
;; equivalent to (foo 1 2 3)
would be equivalent to
def foo(a, b, c): return a + b + c
foo(1, *[2, 3]) ;; equivalent to foo(1, 2, 3)
With these, creating a fold that accepts multiple arguments is just a programming exercise:
(define (my-fold proc accum required-first-list . list-of-optional-lists)
... IMPLEMENT FOLD HERE ...)
Note that if you read the source code of Racket (which uses Chez Scheme), you will see that it uses case-lambda instead of defining the function directly. case-lambda is just a way to make the code more efficient for common usage of fold (i.e., a fold with only one list).

multiple filters in a single iteration

Suppose I have a list of tuples like so:
[["type_2" "val_x"] ["type_1" "val_y"] ["type_1" "val_z"]]
I'd like to filter them, so that I have two separate collections like this:
[["type_2" "val_x"]]
[["type_1" "val_y"] ["type_1" "val_z"]]
I can run filter twice. I'm wondering if it's possible to achieve the same result in a single iteration with functional programming?
This is the desired interface:
(multiple-filter predicate_fn_1 predicate_fn_2 coll)
while (vals (group-by first... would work ok in your case, it is not universal. Here is a variant (one of many possible ones) of applying multiple filters:
(defn classify [items & preds]
(loop [[x & xs :as items] items
res (repeat (count preds) [])]
(if (empty? items)
res
(recur xs
(mapv #(if (% x) (conj %2 x) %2) preds res)))))
in repl:
user> (classify [[:a 10] [:a 20] [:b 30] [:d 2] [:c 40] [:d 1]]
#(= (first %) :a)
#(= (first %) :b)
#(= (first %) :d))
[[[:a 10] [:a 20]] [[:b 30]] [[:d 2] [:d 1]]]
or the same with reduce:
(defn classify [items & preds]
(reduce (fn [res x] (mapv #(if (% x) (conj %2 x) %2) preds res))
(repeat (count preds) [])
items))
The classify function by #leetwinski fails to satisfy your desired interface; as an example, here is a compliant implementation:
(defn multiple-filter [& preds-and-coll]
(let [[preds coll] ((juxt drop-last last) preds-and-coll)]
(mapv #(filterv % coll) preds)))
Example:
(multiple-filter (comp #{"type_1"} first)
(comp #{"type_2"} first)
[["type_2" "val_x"] ["type_1" "val_y"] ["type_1" "val_z"]])
;;=> [[["type_1" "val_y"] ["type_1" "val_z"]] [["type_2" "val_x"]]]
I haven't implemented this as a single iteration because that would complicate this answer and not affect the algorithmic complexity, but feel free to replace my implementation using mapv and filterv with #leetwinski's single-iteration implementation.

contract violation in my implementation of "map"

I'm beginning in Scheme (actually, Racket with DrRacket) and I want to practice by implementing a map function (apply a function to all elements of a list), but there's something wrong that I don't understand.
(I have, aside from my imperative background, a basic knowledge of haskell)
I want to translate the following piece of haskell (Just to show the algorithm) :
map f [] = []
map f x:xs = (f x) : (map f xs)
Here's my code :
(define (map f xs)
(if (= xs '()) '() ; if list is empty, return empty list
(cons (f (car xs)) (map f (cdr xs))))
)
To test it, I used this :
(define (testFunction x) (+ x 1))
(define testList '(1 2 3 4 5))
(map testFunction testList)
And I get the following error :
=: contract violation
expected: number ?
given : '(1 2 3 4 5)
argument position: 1st
other arguments...:
which highlights the predicate (= xs '())
Any tips ?
The = function is specifically for equality between numbers. It has special handling for numeric values by handling comparisons between exact and inexact numbers. In general, though, for non-numeric equality, you should use the equal? predicate:
> (equal? '() '())
#t
In this particular case, as mentioned by Raghav, you can also use empty? or null? to test for the empty list (the empty? predicate is just an alias for null?).
Wow - a few others already beat me to it, but I'll share my answer, anyways.
Your issue stems from your use of = to test list emptiness.
From the = in the docs:
Returns #t if all of the arguments are numerically equal, #f
otherwise.
In order to get your program working, I'd suggest using equal? to test the two lists for equality or, better yet, use empty? or null? to test if xs is an empty list. (I hope you don't take offense, but I've also massaged the code into what's (arguably) more idiomatic Scheme).
(define (mymap f xs)
(if (empty? xs)
xs
(cons
(f (car xs))
(mymap f (cdr xs)))))
(define (test-function x) (+ x 1))
(define test-list (list 1 2 3 4))
(mymap test-function test-list)
If you're using DrRacket, then for that condition, simply use (empty?):
(if (empty? xs)
xs ; because xs is empty
...)

Clojure Group Sequential Occurrences - Improve Function

I'm trying to group items that appear directly beside each other, so long as they are each in a given "white-list". Groupings must have at least two or more items to be included.
For example, first arg is the collection, second arg the whitelist.
(group-sequential [1 2 3 4 5] [2 3])
>> ((2 3))
(group-sequential ["The" "quick" "brown" "healthy" "fox" "jumped" "over" "the" "fence"]
["quick" "brown" "over" "fox" "jumped"])
>> (("quick" "brown") ("fox" "jumped" "over"))
(group-sequential [1 2 3 4 5 6 7] [2 3 6])
>> ((2 3))
This is what I've come up with:
(defn group-sequential
[haystack needles]
(loop [l haystack acc '()]
(let [[curr more] (split-with #(some #{%} needles) l)]
(if (< (count curr) 2)
(if (empty? more) acc (recur (rest more) acc))
(recur (rest more) (cons curr acc))))))
It works, but is pretty ugly. I wonder if there's a much simpler idiomatic way to do it in Clojure? (You should have seen the fn before I discovered split-with :)
I bet there's a nice one-liner with partition-by or something, but it's late and I can't quite seem to make it work.
(defn group-sequential [coll white]
(->> coll
(map (set white))
(partition-by nil?)
(filter (comp first next))))
... a tidier version of Diego Basch's method.
Here's my first attempt:
(defn group-sequential [xs wl]
(let [s (set wl)
f (map #(if (s %) %) xs)
xs' (partition-by nil? f)]
(remove #(or (nil? (first %)) (= 1 (count %))) xs')))
(defn group-sequential
[coll matches]
(let [matches-set (set matches)]
(->> (partition-by (partial contains? matches-set) coll)
(filter #(clojure.set/subset? % matches-set))
(remove #(< (count %) 2)))))
Ok, I realized partition-by is pretty close to what I'm looking for, so I created this function which seems a lot more in line with the core stuff.
(defn partition-if
"Returns a lazy seq of partitions of items that match the filter"
[pred coll]
(lazy-seq
(when-let [s (seq coll)]
(let [[in more0] (split-with pred s)
[out more] (split-with (complement pred) more0)]
(if (empty? in)
(partition-if pred more)
(cons in (partition-if pred more)))))))
(partition-if #(some #{%} [2 3 6]) [1 2 3 4 5 6 7])
>> ((2 3))

How to write a shortest and most idiomatic CLI calculator in Clojure

I like to learn a new language by making small tool like calculator.
Although I already searched a lot idiomatic examples about specific cases(such as idiomatic usage of array and list), I have no idea how to put those together to write this small calculator in an idiomatic way.
So here is my code:
(defn pre-process [s]
"Seperate operands with operators and replace ( with l, ) with r"
(re-seq #"\d+|[\+\-\*\/lr]"
(clojure.string/replace s #"\(|\)" {"(" "l" ")" "r"})))
(defn calc-once [stk]
"Take one operator from operator stack and apply it to
top two numbers in operand stack"
(let [opt (:opt stk)
num (:num stk)
tmp-num (pop (pop num))
tmp-opt (pop opt)
last-two-num [(peek (pop num)) (peek num)]
last-opt (peek opt)]
(assoc stk
:num (conj tmp-num (apply (eval last-opt) last-two-num))
:opt tmp-opt)))
(defn clean-stk [stk]
(loop [stk stk]
(if (> (count (:opt stk)) 1)
(recur (calc-once stk))
(peek (:num stk)))))
(defn calc
"A simple calculator"
[s]
(clean-stk
(reduce
(fn [stk item]
(let [item (read-string item)
operators #{'+ '- '* '/}
prio {'+ 0 ; Define operator priority here
'- 0
'* 1
'/ 1
'l -1
'r -1
'dummy -2}
add-to-num #(assoc %1 :num (conj (:num %1) %2))
add-to-opt #(assoc %1 :opt (conj (:opt %1) %2))
item-prio (get prio item)
last-prio #(get prio (peek (:opt %)))]
(cond
(number? item) ; It's number
(add-to-num stk item)
(get operators item) ; It's operator
(loop [stk stk]
(if (<= item-prio (last-prio stk))
(recur (calc-once stk))
(add-to-opt stk item)))
(= 'l item) ; (
(add-to-opt stk item)
(= 'r item) ; )
(loop [stk stk]
(if (not= (peek (:opt stk)) 'l)
(recur (calc-once stk))
(assoc stk :opt (pop (:opt stk)))))
:else
(println "Unexpected syntax: " item))))
(apply (partial list {:num '() :opt '(dummy)}) ;; Basic structure of stack
s))))
After calling it:
(calc (pre-process (read-line))))
It can calculate like:
(1 + 3) * ( 4 + 4)
32
I think my code could be improved by
eliminating those cond
or
try to make the {:num '() :opt '()} into a more accessible data
structure
, but I have no idea.
Hopefully someone can give me some suggestions or point out problems with my code (or the grammers of my question :P).
====================================Thank you :)================================
Thank you guys for help. I modified my code, it seems better now. But I still have some questions:
Should I put some less generic functions (such as add-to-num) into global var?
Does anybody discover that sometimes naming a function in FP is pretty hard? Especially for those non-generic functions.
And here is my new code:
(def prio
{'+ 0 ; Define operator priority here
'- 0
'* 1
'/ 1
'l -1
'r -1
'dummy -2})
(def operators #{'+ '- '* '/})
(defn pre-process [s]
"Seperate operands with operators and replace ( with l, ) with r"
(re-seq #"\d+|[\+\-\*\/lr]"
(clojure.string/replace s #"\(|\)" {"(" "l" ")" "r"})))
(defn calc-once [stk]
"Take one operator from operator stack and apply it to
top two numbers in operand stack"
(let [opt (:opt stk)
num (:num stk)
tmp-num (pop (pop num))
tmp-opt (pop opt)
last-two-num [(peek (pop num)) (peek num)]
last-opt (peek opt)]
(assoc stk
:num (conj tmp-num (apply (eval last-opt) last-two-num))
:opt tmp-opt)))
(defn process-stk [stk checker fn-ret]
(loop [stk stk]
(if (checker stk)
(recur (calc-once stk))
(fn-ret stk))))
(defn calc
"A simple calculator"
[s]
(process-stk
(reduce
(fn [stk item]
(let [item (read-string item)
add-to-num #(assoc %1 :num (conj (:num %1) %2))
add-to-opt #(assoc %1 :opt (conj (:opt %1) %2))
item-prio (get prio item)
last-prio #(get prio (peek (:opt %)))]
(cond
(number? item) ; It's number
(add-to-num stk item)
(get operators item) ; It's operator
(process-stk stk #(<= item-prio (last-prio %))
#(add-to-opt % item))
(= 'l item) ; (
(add-to-opt stk item)
(= 'r item) ; )
(process-stk stk #(not= (peek (:opt %)) 'l)
#(assoc % :opt (pop (:opt %))))
:else
(println "Unexpected syntax: " item))))
(apply (partial list {:num '() :opt '(dummy)}) ;; Basic structure of stack
s))
#(> (count (:opt %)) 1)
#(peek (:num %))))
This cries out for a macro solution, given below. I did cheat in that there are only 2 precedence levels so I didn't have to work out a stack to keep track of precedence. This solution could be generalized but it take a little more doing.
The trick to remember about macros in clojure is they take clojure structure (which is a nested list of lists) and return a different list of lists. The calc macro simply takes the input, wraps it in parens and passes it to the clojure reader which does all the heavy lifting of parsing the input string into a list of symbols.
Then the reorder-equation function turns the infix into a prefix order list. That list is returned by the macro and is then evaluated as clojure code.
The check for * and / makes sure they get evaluated first. To see what it does try
(reorder-equation '((1 + 3) * (4 + 4)))
=> (* (+ 1 3) (+ 4 4))
As you can see it takes the equations and rewrites it into a valid clojure expression which will then be evaluated.
This may seem like cheating but as you get more familiar with Clojure you will realize that you can let the language do a lot of the heavy lifting. Parsing input into a list of symbols and using those symbols as function names make perfect sense. As a matter of fact, any function that takes two arguments is valid in our calculator:
(calc "(1 + 3) < (4 + 4)")
=> true
and
(calc "(1 + 3) str (4 + 4)")
=> "48"
The code:
(defn reorder-equation [ arg ]
(if (seq? arg)
(let [[f s & r] arg
f (reorder-equation f)]
(cond
(#{"*" "/"} (str s)) ( let [[t ft & r2 ] r
t (reorder-equation t)]
(if ft
(list ft (list s f t) (reorder-equation r2))
(list s f t)))
(nil? s) f
:else (list s f (reorder-equation r))))
arg))
(defmacro calc [inp]
(let [tr (read-string (str "(" inp ")"))]
(reorder-equation tr)))
It is the correct version of the M Smith's solution, although I used eval in my code, which is generally a bad idea. I paste it here and hope it can help someone.
(defn calc [ arg ]
(if (seq? arg)
(let [[f s & r] arg
f (calc f)]
(if (nil? s)
f
(let [[t ft & r2 ] r
t (calc t)
new-f ((resolve s) f t)]
(cond
(#{"*" "/"} (str s))
(if ft
(calc (concat `(~new-f) (rest r)))
new-f)
(nil? s) f
:else
(if (#{"+" "/"} (str ft))
(calc (concat `(~new-f) (rest r)))
((resolve s) f (calc r)))))))
arg))
(defn main [inp]
(let [tr (read-string (str "(" inp ")"))]
(calc tr)))
Example:
(println (main "2 - 4 + 8 * 16"))
(println (main "(1 + 2) * (10 - 4) / 9 * 6"))
(println (main "10 + 2 * 100 / ((40 - 37) * 100 * (2 - 4 + 8 * 16))"))
Result:
126
12
1891/189
Here is my solution, which does not use regex or macros, and which instead uses partition and reduce for its parsing logic.
The general idea is that you treat the user input as a sequence of symbol pairs after the initial value. So your arithmetic expression is essentially '(<init-value> (op1 value1) (op2 value2) ...(opN valueN)) Of course, the <init-value> could itself be a parenthetical, and if so must first be reduced as well.
partition then provides the sequence of symbol/value pairs to reduce, which constructs a valid Clojure expression with symbols arranged by precedence. I halt evaluation on invalid symbols (anything not a number list or symbol), exiting the reduce block with the handy reduced (added in 1.5).
An important concept is that any lists (parenthesis) encountered ultimately reduce to values, and so are recursively reduce-d. The function peel handles nested lists, i.e. (((1 + 1)))
It a little verbose (I prefer descriptive variable names), but it's correct. I checked several rather complex nested expressions against Google.
(def instructions
(str "Please enter an arithmetic expression separated by spaces.\n"
"i.e. 1 + 2 / 3 * 4"))
(defn- error
([] (error instructions))
([msg] (str "ERROR: " (if (nil? msg)
instructions
msg))))
(def ^{:private true} operators {'* 1
'/ 1
'+ 0
'- 0})
(def ^{:private true} operator? (set (keys operators)))
(defn- higher-precedence? [leftop rightop]
(< (operators leftop) (operators rightop)))
(declare parse-expr)
(defn- peel
"Remove all outer lists until you reach
a list that contains more than one value."
[expr]
(if (and (list? expr) (= 1 (count expr)))
(recur (first expr))
expr))
(defn- read-value [e]
(if (list? e)
(parse-expr (peel e))
(if (number? e) e)))
(defn- valid-expr? [op right]
(and (operator? op)
(or (number? right) (list? right))))
(defn- higher-precedence-concat [left op right]
(let [right-value (read-value right)
last-left-value (last left)
other-left-values (drop-last left)]
(concat other-left-values `((~op ~last-left-value ~right-value)))))
(defn- parse-expr [s]
(let [left (read-value (first s))
exprs (partition 2 (rest s))
[[op right] & _] exprs]
(if (and left (valid-expr? op left))
(let [right (read-value right)]
(reduce (fn [left [op right]]
(if (valid-expr? op right)
(if (higher-precedence? (first left) op)
(higher-precedence-concat left op right)
(list op left (read-value right)))
(reduced nil)))
(list op left right) (rest exprs))))))
(defn calc [input]
(try
(let [expr (-> (str "(" input ")")
read-string ;; TODO: use tools.reader?
peel)]
(if (list? expr)
(if-let [result (eval (parse-expr expr))]
result
(error))
(error)))
(catch java.lang.RuntimeException ex
(error (.getMessage ex)))))
Example checked against google's online calculator:
(calc "10 + 2 * 100 / ((40 - 37) * 100 * (2 - 4 + 8 * 16))")
=> 1891/189
(double *1)
=> 10.00529100529101
Two limitations: expressions must be space delimited (i.e. 1+2-3 not supported) just like Incanter's infix mathematics, and because I use read-string the user input can have trailing parens (I consider this a bug I'll have to fix with a more robust REPL implementation).
Credits: I used Eric Robert's Programming Abstractions in C (Addison Wesley, 1997) as a reference in coding the above. Chapter 14 "Expression Trees" describes an almost identical problem.
I'll try it out, but I can't get your code to work, so it's a bit hard for me to understand what is happening in every place. Basically, the following is a guess and not intended to be a complete answer. Hopefully someone can come in and edit this down a bit and get it to function correctly.
I'll start with the basic premise: You have, in my opinion, way to many nested and anonymous functions. Everywhere you see a #(xyz) could probably be pulled out into its own function. I'm pretty sure having function inside of function inside of function would be pretty bad form in any programming language, and I feel it is bad form here. I began by removing anon functions, both hashed and the (fn) you have in your original code.
I also don't like nesting functions in my let-bindings.
(def prio
{'+ 0 ; Define operator priority here
'- 0
'* 1
'/ 1
'l -1
'r -1
'dummy -2})
(def operators #{'+ '- '* '/})
(defn pre-process [s]
"Seperate operands with operators and replace ( with l, ) with r"
(re-seq #"\d+|[\+\-\*\/lr]"
(clojure.string/replace s #"\(|\)" {"(" "l" ")" "r"})))
(defn calc-once [stk]
"Take one operator from operator stack and apply it to
top two numbers in operand stack"
(let [opt (:opt stk)
num (:num stk)
tmp-num (pop (pop num))
tmp-opt (pop opt)
last-two-num [(peek (pop num)) (peek num)]
last-opt (peek opt)]
(assoc stk
:num (conj tmp-num (apply (eval last-opt) last-two-num))
:opt tmp-opt)))
(defn process-stk [stk checker fn-ret]
(loop [stk stk]
(if (checker stk)
(recur (calc-once stk))
(fn-ret stk))))
(defn assoc-to-item [item]
#(assoc %1 item (conj (item %1) %2)))
(defn priority [item]
(get prio item))
(defn create-checker [op item v]
(op item v))
(defn pre-calc [stk item s]
(reduce
(let [item (read-string item)
add-to-num (assoc-to-item :num)
add-to-opt (assoc-to-item :opt)
item-prio (priority item)
last-prio (priority (last (:opt)))]
(cond
(number? item) ; It's number
(add-to-num stk item)
(get operators item) ; It's operator
(process-stk stk
(create-checker <= item-prio (last-prio))
add-to-opt)
(= 'l item) ; (
(add-to-opt stk item)
(= 'r item) ; )
(process-stk stk
(create-checker not= (peek (:opt)) 'l)
#(assoc % :opt (pop (:opt %))))
:else
(println "Unexpected syntax: " item))))
(apply (partial list {:num '() :opt '(dummy)}) ;; Basic structure of stack
s))
(defn calc [s]
"A simple calculator"
(process-stk (pre-calc stk item s)
#(> (count (:opt %)) 1)
#(peek (:num %))))
Further notes:
(peek) is very ambiguous and I generally don't like using it. From the cheatsheets:
For a list or queue, same as first, for a vector, same as, but much
more efficient than, last. If the collection is empty, returns nil.
Since I'm not entirely sure what structure you are working with at all times (I think its a vec?) and you do, you may want to use last or first, which ever is more appropriate. Although it is "much more efficient" than last, it's not helping me understand how the program works, so use peek in the finished product but not the shared product (mind you don't really need super speed for this either).
I also think that the (cond) should be unambiguously case-tested.
I attempted to make it a tad more "idiomatic" by making sure the args are less ambiguous. In your original code, you are passing in massive functions (and the results of nested functions) as one large argument to another function. Breaking all of that down to smaller functions is where you need to work more a bit. Notice how it is more clear what is happening in the calc function?
I pulled out the anon function inside calc and entered into a function called pre-calc. I would still suggest pulling out the anon functions from calc and work on clarifying what is happening inside of pre-calc. It is still hard to read because I can't really guess what is happening.
I would suggest starting with something like the following because it is hard to see what args are passed into (reduce). You can see how this is confusing because I am passing item in as an argument then I am following your pattern and passing item into (read-string) and then I am binding that result to item. I'm not sure if this is your intent, but I most certainly would not pass in an arg called let and them bind the result of passing it into a function created by evaluating item. This creates further confusion for me because you have item passed into a let-bound item-prio. I never did this, so I don't even know if the arg item or the let-bound item is being evaluated here.
Here is that part of the code. Notice how it is easy to see what is being reduced now?
(defn stack-binding [item]
(let [item (read-string item)
add-to-num (assoc-to-item :num)
add-to-opt (assoc-to-item :opt)
item-prio (priority item)
last-prio (priority (last (:opt)))]
(cond
(number? item) ; It's number
(add-to-num stk item)
(get operators item) ; It's operator
(process-stk stk
(create-checker <= item-prio (last-prio))
add-to-opt)
(= 'l item) ; (
(add-to-opt stk item)
(= 'r item) ; )
(process-stk stk
(create-checker not= (peek (:opt)) 'l)
#(assoc % :opt (pop (:opt %))))
:else
(println "Unexpected syntax: " item))))
(defn pre-calc [stk item s]
(reduce (stack-binding item)
(apply (partial list {:num '() :opt '(dummy)}) ;; Basic structure of stack
s))
There is a lot more I could write, but as I said, I really don't know how everything is working together. Regardless, this should at least show some of the logic I would use in creating this program. I would try to generalize this a lot more and keep it so that each function is only about 10 LOC each.
As I said, I hope others can either expand on this or edit it to something more palatable.
The smallest idiomatic calculator is the REPL!
If infix notation is the goal, I'd go for changing the reader so that numbers become functions of the arithmetic functions *,/,+,-,% etc, so (7 + 5) would be read as 7, being a Clojure function (in addition to being a java.lang.Number), can take + 5 as arguments, similar to how, in Smalltalk, numbers can understand arithmetic operations as messages.

Resources