I'm building a program that's meant to allow users count either the number of letters or number of words in a string, however when running the program through the cmd, I'm getting a clojure.lang.ArityException, wrong number of args (1) passed to : core/-main/counter--5333
My code is
;;Create a GUI which allows user to input a string and to select "word count" or "letter count". When "Start" is clicked pass both the string and either (wordCount [string x]) or (letterCount [string x]) to
;;declare functions as variables
;;show function that takes functions as parameters
;;show function that returns another function
(ns firstclass.core
(:gen-class)
(:use seesaw.core))
(defn -main
[& args]
(def strInput (input "Please enter a string to be evaluated"))
(def groups (button-group))
(def s (selection groups))
(def letterRadio (radio :text "Letter" :group groups))
(def wordRadio (radio :text "Word" :group groups))
(defn letterCount
[string]
(loop [characters string
a-count 0]
(if (= (first characters) \a)
(recur (rest characters) (inc a-count))
a-count)))
(defn wordCount
[string]
(loop [characters string
a-count 0]
(if (= (first characters) \a)
(recur (rest characters) (inc a-count))
a-count)))
(def counter (fn [fn x, string strInput] (x [strInput])))
(defn handler [event]
(if-let [s letterRadio]
(counter [letterCount, strInput]))
(if-let [s wordRadio]
(counter [wordCount, strInput])))
(def start (button :text "Start Count" :listen [:action handler] ))
(def panel
(flow-panel :items [strInput, letterRadio, wordRadio, start]))
(invoke-later
(-> (frame :content panel :on-close :dispose)
pack! show!)))
for your definition of counter
(def counter (fn [fn x, string strInput] (x [strInput])))
you have a function of four arguments
in the handler function, you call it with one argument:
(counter [letterCount strInput])
from context I assume that you meant to define counter as having two arguments, and you meant to call it on two arguments, rather than a single vector of two items.
(def counter (fn [x strInput] (x strInput)))
...
(counter letterCount strInput)
Also, it's better to use defn to define functions, rather than def and fn separately
(defn counter [x strInput] (x strInput))
Related
I'm a newbie to Clojure. I think I'm trying to solve this procedurally and there must be a better (more functional) way to do this in Clojure...
The -main function can receive a variable number of 'args'. I would like to print them out but avoid an IndexOutOfBoundsException. I thought I could mimic Java and use a case fall-through to minimize the code involved, but that didn't work:
(defn -main [& args]
(println "There are" (str (count args)) "input arguments.")
(println "Here are args:" (str args))
(let [x (count args)]
(case x
(> x 0) (do
(print "Here is the first arg: ")
(println (nth args 0)))
(> x 1) (do
(print "Here is the 2nd arg: ")
(println (nth args 1)))
(> x 2) (do
(print "Here is the 3rd arg: ")
(println (nth args 2))))))
(doseq [[n arg] (map-indexed vector arguments)]
(println (str "Here is the argument #" (inc n) ": " (pr-str arg))))
map-indexes is like map but adds index number in the beginning.
So it goes item by item through arguments, packs index and item into a vector and by destructruing index number and item are mapped to [n arg].
Since clojure begins counting from 0, you use (inc n) to begin counting from 1. pr-str is pretty print string. The str joins all string components together.
there is also a handy formatting facility in clojure's core library: cl-format, which is the port of common lisp's format syntax. It includes a nice way to print out collections:
(require '[clojure.pprint :refer [cl-format]])
(let [args [:a :b :c :d]]
(cl-format true "~{here is the ~:r arg: ~a~%~}"
(interleave (rest (range)) args)))
;; here is the first arg: :a
;; here is the second arg: :b
;; here is the third arg: :c
;; here is the fourth arg: :d
some more information about what format can do is here
I am writing a function that, for any given string, replaces any digits within that String with the same number of '.' characters.
Examples:
AT2X -> AT..X
QW3G45 -> QW...G.........
T3Z1 -> T...Z.
I've written the following Clojure function but I am getting an error I don't quite understand:
java.lang.ClassCastException: clojure.lang.LazySeq (in module: Unnamed Module) cannot be case to java.lang.Charsequence
I'm interpreting from the error that I need to force an evaluation of a lazy sequence back into a String (or CharSequence) but I can't figure out where to do so or if this is correct.
(defn dotify
;;Replaces digits with the same number of '.'s for use in traditional board formats
[FEN]
(let [values (doall (filter isDigit (seq FEN)))]
(fn [values]
(let [value (first values)]
(str/replace FEN value (fn dots [number]
(fn [s times]
(if (> times 0)
(recur (str s ".") (dec times)))) "" (Character/digit number 10)) value))
(recur (rest values))) values))
There is a standard clojure.string/replace function that may handle that case. Its last argument might be not just a string or a pattern but also a function that turns a found fragment into what you want.
Let's prepare such a function first:
(defn replacer [sum-str]
(let [num (read-string num-str)]
(apply str (repeat num \.))))
You may try it in this way:
user> (replacer "2")
..
user> (replacer "9")
.........
user> (replacer "22")
......................
user>
Now pass it into replace as follows:
user> (clojure.string/replace "a2b3c11" #"\d+" replacer)
a..b...c...........
Here's a way to do this using reduce:
(defn dotify [s]
(->> s
(reduce (fn [acc elem]
(if (Character/isDigit elem)
(let [dots (Integer/parseInt (str elem))]
(apply conj acc (repeat dots \.)))
(conj acc elem)))
[])
(apply str)))
(dotify "zx4g1z2h")
=> "zx....g.z..h"
And another version using mapcat:
(defn dotify-mapcat [s]
(apply str
(mapcat (fn [c]
(if (Character/isDigit c)
(repeat (Integer/parseInt (str c)) \.)
[c]))
s)))
There are some issues in your example:
Many of the internal forms are themselves functions, but it looks like you just want their bodies or implementations instead of wrapping them in functions.
It's hard to tell by the indentation/whitespace, but the entire function is just recur-ing, the fn above it is not being used or returned.
One of the arguments to str/replace is a function that returns a function.
It helps to break the problem down into smaller pieces. For one, you know you'll need to examine each character in a string and decide whether to just return it or expand it into a sequence of dots. So you can start with a function:
(defn expand-char [^Character c]
(if (Character/isDigit c)
(repeat (Integer/parseInt (str c)) \.)
[c]))
Then use that function that operates on one character at a time in a higher-order function that operates on the entire string:
(apply str (mapcat expand-char s))
=> "zx....g.z..h"
Note this is also ~5x faster than the examples above because of the ^Character type-hint in expand-char function.
You can do this with str/replace too:
(defn expand-char [s]
(if (Character/isDigit ^Character (first s))
(apply str (repeat (Integer/parseInt s) \.))
s))
(str/replace "zx4g1z2h" #"." expand-char)
=> "zx....g.z..h"
I'm trying to write a function that counts the number of vowels and consonants in a given string. The return value is a map with two keys, vowels and consonants. The values for each respective key are simply the counts.
The function that I have been able to develop so far is
(defn count-vowels-consenants [s]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(for [xs s]
(if
(contains? v (str xs))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)
))
#m))
however (count-vowels-consenants "sldkfjlskjwe") returns {"vowels":0 "consenants": 0}
What am I doing wrong?
EDIT: changed my input from str to s as str is a function in Clojure.
I think for is lazy so you're not going to actually do anything until you try to realize it. I added a first onto the for loop which realized the list and resulted in an error which you made by overwriting the str function with the str string. Ideally, you would just do this without the atom rigmarole.
(defn count-vowels-consonants [s]
(let [v #{\a \e \i \o \u}
vowels (filter v s)
consonants (remove v s)]
{:consonants (count consonants)
:vowels (count vowels)}))
if the atom is what you want, then use doseq instead of for and it will update the atom for everything in the string. also make sure you don't overwrite the str function by using it in your function binding.
if this side effecting scheme is inevitable (for sume educational reason, i suppose) just replace for with doseq which is a side effecting eager equivalent of for
(by the way: there is a mistake in your initial code: you use str as an input param name, and then try to use it as a function. So you are shadowing the def from the clojure.core, just try to avoid using params named like the core functions):
(defn count-vowels-consenants [input]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(doseq [s input]
(if (contains? v (str s))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)))
#m))
#'user/count-vowels-consenants
user> (count-vowels-consenants "asdfg")
;; {"vowels" 1, "consenants" 4}
otherwise you could do something like this:
user> (reduce #(update %1
(if (#{\a \e \i \o \u} %2)
"vowels" "consonants")
(fnil inc 0))
{} "qwertyui")
;;{"consonants" 5, "vowels" 3}
or
user> (frequencies (map #(if (#{\a \e \i \o \u} %)
"vowels" "consonants")
"qwertyui"))
;;{"consonants" 5, "vowels" 3}
or this (if you're good with having true/false instead of "vowels/consonants"):
user> (frequencies (map (comp some? #{\a \e \i \o \u}) "qwertyui"))
;;{false 5, true 3}
for is lazy as mentioned by #Brandon H. You can use loop recur if you want. Here I change for with loop-recur.
(defn count-vowels-consenants [input]
(let [m (atom {"vowels" 0 "consenants" 0})
v #{"a" "e" "i" "o" "u"}]
(loop [s input]
(when (> (count s) 0)
(if
(contains? v (first (str s) ))
(swap! m update-in ["vowels"] inc)
(swap! m update-in ["consenants"] inc)
))
(recur (apply str (rest s))))
#m))
The question, and every extant answer, assumes that every character is a vowel or a consonant: not so. And even in ASCII, there are lower and upper case letters. I'd do it as follows ...
(defn count-vowels-consonants [s]
(let [vowels #{\a \e \i \o \u
\A \E \I \O \U}
classify (fn [c]
(if (Character/isLetter c)
(if (vowels c) :vowel :consonant)))]
(map-v count (dissoc (group-by classify s) nil))))
... where map-v is a function that map's the values of a map:
(defn map-v [f m] (reduce (fn [a [k v]] (assoc a k (f v))) {} m))
For example,
(count-vowels-consonants "s2a Boo!")
;{:vowel 3, :consonant 2}
This traverses the string just once.
Today I tried to implement a "R-like" melt function. I use it for Big Data coming from Big Query.
I do not have big constraints about time to compute and this function takes less than 5-10 seconds to work on millions of rows.
I start with this kind of data :
(def sample
'({:list "123,250" :group "a"} {:list "234,260" :group "b"}))
Then I defined a function to put the list into a vector :
(defn split-data-rank [datatab value]
(let [splitted (map (fn[x] (assoc x value (str/split (x value) #","))) datatab)]
(map (fn[y] (let [index (map inc (range (count (y value))))]
(assoc y value (zipmap index (y value)))))
splitted)))
Launch :
(split-data-rank sample :list)
As you can see, it returns the same sequence but it replaces :list by a map giving the position in the list of each item in quoted list.
Then, I want to melt the "dataframe" by creating for each item in a group its own row with its rank in the group.
So that I created this function :
(defn split-melt [datatab value]
(let [splitted (split-data-rank datatab value)]
(map (fn [y] (dissoc y value))
(apply concat
(map
(fn[x]
(map
(fn[[k v]]
(assoc x :item v :Rank k))
(x value)))
splitted)))))
Launch :
(split-melt sample :list)
The problem is that it is heavily indented and use a lot of map. I apply dissoc to drop :list (which is useless now) and I have also to use concat because without that I have a sequence of sequences.
Do you think there is a more efficient/shorter way to design this function ?
I am heavily confused with reduce, does not know whether it can be applied here since there are two arguments in a way.
Thanks a lot !
If you don't need the split-data-rank function, I will go for:
(defn melt [datatab value]
(mapcat (fn [x]
(let [items (str/split (get x value) #",")]
(map-indexed (fn [idx item]
(-> x
(assoc :Rank (inc idx) :item item)
(dissoc value)))
items)))
datatab))
What's the problem with following code:
the func expression get expression that contains term that can contain expression...
(defn term[]
(def mytmp (zip/xml-zip {:tag :term}))
(cond
(= (first(:content(first vecTok))) "(")
(do
(def mytmp (popVecTo mytmp))
(def mytmp (zip/append-child mytmp (expression)))
(def mytmp (popVecTo mytmp)))
:else
(def mytmp (popVecTo mytmp)))
(zip/node mytmp))
(defn expression[]
(def mytmp (zip/xml-zip {:tag :expression}))
(def mytmp (zip/append-child mytmp (term)))
(while (contains? #{"+", "-", "*","/", "&", "|", "<", ">", "="} (first(:content(first vecTok))) )
(do
(def mytmp (popVecTo mytmp))
(def mytmp (zip/append-child mytmp (term)))))
(zip/node mytmp))
(def vecTok (vec (:content(first(xml-seq (parse "C:/Users/User/Desktop/forHekronot/BallT.xml"))))))
In the file :
<a><symbol>(</symbol><identifier>dy</identifier><symbol>-</symbol><identifier>dx</identifier><symbol>)</symbol></a>
Notwithstanding #jszakmeister's comment on how to better solve the problem, let me try to give and answer to the question:
you can first (def expression) and then (defn term [] ...) and finally (defn expression [] ...).
The classic example for indirect recursion is of course the poor man's odd/even function for positive numbers:
clojurec.core=> (def even)
#'clojurec.core/even
clojurec.core=> (defn odd [x] (and (not (= x 0)) (even (dec x))))
#'clojurec.core/odd
clojurec.core=> (defn even [x] (or (= x 0) (odd (dec x))))
#'clojurec.core/even
clojurec.core=> (even 10)
true
clojurec.core=> (odd 10)
false
clojurec.core=> (odd 10000)
StackOverflowError clojure.lang.Numbers.equal (Numbers.java:214)
Ooops, depending on the size (or rather depth) of your file this could be a problem. But not all is lost, we can redefine even to use trampoline internally:
(defn even [n]
(letfn [(evenrec [x] (or (= x 0) #(oddrec (dec x))))
(oddrec [x] (and (not (= x 0)) #(evenrec (dec x))))]
(trampoline evenrec n)))