Convert input string into Morse Code with Clojure - functional-programming

I am trying to develop a converter that takes an input string and converts it into morse code through a Morse Library map, while also respecting functional programming rules. Sorry for any clarification issues, I am new to Stack Overflow
(ns clojureassignment.core
(:gen-class))
(require '[clojure.string :as str])
;this function is where the converter is developed
(defn morse->ASCI
[x]
(def morse_Library {:A ".-":B "-...":C "-.-.":D "-..":E ".":F "..-.":G "--.":H "...."
:I "..":J ".---"
:K "-.-":L ".-..":M "--" :N "-.":O "---":P ".--.":Q "--.-":R ".-."
:S "...":T "-":U "..-":V "...-":W ".--":X "-..-":Y "-.--":Z "--.."
:0 "-----":1 ".----":2 "..---":3 "...--":4 "....-":5 "....."
:6 "-....":7 "--...":8 "---..":9 "----."})
(let [stringVector (str/upper-case(seq x))] ;divide the string into a sequence of characters
;trying to create iteration of the input where it checks if its value is found in the morse library
(doseq [[stringVector] (morse_Library)]
(if (= stringVector (morse_Library)
(do (println(str (key morse_Library))))
(do (println("characters not found"))))
)))
(print (str/upper-case stringVector))
)
(defn -main
[& args]
(println "ASCII to Morse Converter.")
(println "Make sure to include whitespaces after each ASCII character. Add String")
(def stringInput (read-line))
(println stringInput )
(morse->ASCI stringInput)
)
(-main)
I tried to create a "doseq" iteration where it checks if the value is found in the map.

Good things:
using a map for your morse translation; maps are also functions and
make it easy to write a transformation like this
Not so good things:
don't def inside other def:s - it's used to define namespace
global things and it is not like var or auto or whatever you
know from other languages
don't use keywords as map keys, if you don't plan to use them like
that; Clojure takes pretty much anything as key. So in your case
use chars
don't seq and then upper-case - wrong order - this will give you
the .toString() from a sequence of characters otherwise
don't name things all the same (stringVector)
doseq is for side-effects; you don't want your morse-code function
to do side-effects (you maybe want to print it later); in functional
programming you shift your side-effects to the edges - this way your
code becomes easier to test and reason about
you pretty much never need :gen-class
use the correct way to require in the
namespace
Clojure prefers snake-case over camelCase
(random rant: if you are using tutorialpoint (I assume this from several
things going wrong here and having them seen there) to learn Clojure: do
yourself a favour and look for another resource; as of now they don't
teach idiomatic Clojure!
Make your transformation a pure function:
upper-case the input
make a seq from it (this now is a sequence of chars) - but you don't
actually have to do this, because the next step will take care of it
map the look-up-table with your designated fallback over each char
join the result
This will look something like this:
(def morse {\A ".-" ,,, })
(->> input
(str/upper-case)
(map #(morse % "???"))
(str/join))

;; preparation of morse map
(ns morse
(:require [clojure.string :as str]))
;; I stole morse dictionary from some python code in:
;; https://www.geeksforgeeks.org/morse-code-translator-python/
(def s "{ 'A':'.-', 'B':'-...',
                    'C':'-.-.', 'D':'-..', 'E':'.',
                    'F':'..-.', 'G':'--.', 'H':'....',
                    'I':'..', 'J':'.---', 'K':'-.-',
                    'L':'.-..', 'M':'--', 'N':'-.',
                    'O':'---', 'P':'.--.', 'Q':'--.-',
                    'R':'.-.', 'S':'...', 'T':'-',
                    'U':'..-', 'V':'...-', 'W':'.--',
                    'X':'-..-', 'Y':'-.--', 'Z':'--..',
                    '1':'.----', '2':'..---', '3':'...--',
                    '4':'....-', '5':'.....', '6':'-....',
                    '7':'--...', '8':'---..', '9':'----.',
                    '0':'-----', ', ':'--..--', '.':'.-.-.-',
                    '?':'..--..', '/':'-..-.', '-':'-....-',
                    '(':'-.--.', ')':'-.--.-'}")
;; and transformed it using clojure to a clojure map:
(def m (read-string (str/replace
(str/replace
(str/replace
(str/replace s
"\n" "")
"                    " " ")
":" " ")
"'" "\"")))
;; now `m` contains the string-to-morse map
The actual answer starts here:
;; convert any text string to a morse string:
(defn string-to-morse [s]
(str/join " "
(map #(get m (str/upper-case %)) (str/split s #""))))
;; and this function can transform the output back to text:
(defn morse-to-string [morse-string]
(let [ms (zipmap (vals m) (keys m))]
(str/join (map #(get (zipmap (vals m) (keys m)) % " ")
(str/split morse-string #" ")))))

Related

Recur not at tail position

How can I use something similiar to recurnot at tail position?
Take a look at my code:
(defn -main [& args]
(println "Hi! Type a file name...")
(defn readFile[])
(let [fileName(read-line)]
(let [rdr (reader fileName)]
(if-not (.exists rdr)
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))
(defn list '())
(doseq [line (line-seq rdr)]
(if-not (= "" line)
(concat list '(line)))
(list))))
(defn fileLinesList (readFile))
...
...)
I know I can't use recur here... But I neither know how can I make it in clojure.
I'm a newbie in Clojure and I'm coming from a OOP context. So...
Is there a way to use recursion in this case?
What would be an alternative?
First of all you should not nest your functions definitions in another defn (-main in this case). defn or def always defines symbol bindings at the top level of namespace and they don't nest. If you want to define a locally scoped function you need to use let and fn, e.g.
(let [my-fn (fn [a b] (+ a b))]
(my-fn 1 2))
In your particular case I think it would be easier to split your code into multiple functions. This way it will be more readable.
Prompting for a file name is one piece of your logic.
(defn get-existing-filename []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
filename
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Then you can use it to read a file removing empty lines:
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
For a file with following content:
AAA
BBB
CCC
DDD
it will return
("AAA" "BBB" "CCC" "DDD")
If you really want it as a single function, the following will work:
(defn read-file []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Finally, this function can be called from -main.
I have also noticed another issue in your sample code:
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
if and if-not require a single expression for their then and else branches. If you want to have multiple expressions you need to nest them in do:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
If you need if or if-not without the else branch then you can use when or when-not macros. Then you don't need to wrap multiple expressions because when/when-not will wrap them for your inside of do.
(when true
(println 1)
(println 2))
is equivalent to
(if true
(do
(println 1)
(println 2)))

Simple Calculator (Addition function input) in Clojure

I'm trying to write a simple calculator with addition, subtraction, etc.
My problem is with getting the user input. How do I turn the string of numerical values into a vector? And also what is a better way to write the program?
(ns scalc.core)
(defn add
[numbers]
(println (apply + numbers)))
(defn numchoose
[]
(println "What numbers?: ")
(let [numbers (read-line)] numbers))
(defn opchoose
[]
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(if (= operation "add")
(do
(println "You chose to add.")
(let [numvect (numchoose)]
(add [numvect]))))))
(defn -main
[& args]
(opchoose)
(numchoose))
And this is the error:
~/clj/scalc 1/7 % lein trampoline run -m scalc.core
What operation would you like to do?:
add
You chose to add.
What numbers?:
5 7
Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Number
at java.lang.Class.cast(Class.java:3005)
at clojure.core$cast.invoke(core.clj:318)
at clojure.core$_PLUS_.invoke(core.clj:927)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at clojure.core$apply.invoke(core.clj:601)
at scalc.core$add.invoke(core.clj:5)
at scalc.core$opchoose.invoke(core.clj:21)
at scalc.core$_main.doInvoke(core.clj:27)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.Var.invoke(Var.java:411)
at user$eval15.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
at clojure.lang.Compiler.eval(Compiler.java:6501)
at clojure.lang.Compiler.eval(Compiler.java:6477)
at clojure.core$eval.invoke(core.clj:2797)
at clojure.main$eval_opt.invoke(main.clj:297)
at clojure.main$initialize.invoke(main.clj:316)
at clojure.main$null_opt.invoke(main.clj:349)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
EDIT: the solved program now looks like this:
(ns scalc.core)
(defn add [numbers]
(reduce + numbers))
(defn numchoose []
(let [nums (re-seq #"\d+" (read-line))]
(map #(Integer/parseInt %) nums)))
(defn delegate []
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(when (= operation "add")
(println "You chose to add.")
(println "What numbers? ")
(add (numchoose)))))
(defn -main
[& args]
(delegate))
For getting the numbers, you can use re-seq:
(re-seq #"\d+" "123 456 789") => ("123" "456" 789")
You still only have strings rather than numbers though. You can use read-string to get the numbers (read-string is convenient, but not safe in all cases. Here we make sure there are really only numbers in these strings so it's fine).
(read-string "5") => 5
Instead of (apply + numbers) you could use reduce: (reduce + numbers), also your add function really shouldn't print anything (you should try to separate functional functions from side-effecty functions whenever possible).
This (let [numbers (read-line)] numbers) is equal to (read-line). Don't overcomplicate things!
Instead of
(if (= operation "add")
(do ... ))
you can write
(when (= operation "add")
...)
when is just a macro that's useful when you don't need the else case in your ifs (it wraps everything after the condition in a do, and evaluates to nil when the condition evaluates to false).

Can you get the "code as data" of a loaded function in Clojure?

To put it another, way, "Okay, so code is data..."
That thread addresses how to read from a source file, but I'm wondering how to get the s-expression of an already-loaded function into a data structure that I can read and manipulate.
In other words, if I say,
(defn example [a b] (+ a b))
can't I get that list at runtime? Isn't this the whole point of "code as data"?
This is really a general Lisp question, but I'm looking for an answer in Clojure.
You can use the clojure.repl/source macro to get the source of a symbol:
user> (source max)
(defn max
"Returns the greatest of the nums."
{:added "1.0"
:inline-arities >1?
:inline (nary-inline 'max)}
([x] x)
([x y] (. clojure.lang.Numbers (max x y)))
([x y & more]
(reduce1 max (max x y) more)))
nil
But this is only part of the answer. AFAICT source looks up the source filename and line number that define the given symbol, and then prints the source code from the file. Therefore, source will not work on symbols that you do not have the source for, i.e. AOT-compiled clojure code.
Coming back to your original question, you can think of source as reading the meta data associated with the given symbol and simply printing that. I.e. it's cheating. It's not in any way returning "code as data" to you, where with code I mean a compiled clojure function.
In my mind "code as data" refers to the feature of lisps where source code is effectively a lisp data structure, and therefore it can be read by the lisp reader. That is, I can create a data structure that is valid lisp code, and eval that.
For example:
user=> (eval '(+ 1 1))
2
Here '(+ 1 1) is a literal list which gets read by the clojure reader and then evaluated as clojure code.
Update: Yehonathan Sharvit was asking in one of the comments if it's possible to modify the code for a function. The following snippet reads in the source for a function, modifies the resulting data structure, and finally evaluates the data structure resulting in a new function, my-nth, being defined:
(eval
(let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
`(~(first src) my-nth ~#(nnext src))))
The syntax-quote line replaces nth with my-nth in the defn form.
You can get the source in recent versions of clojure with the source function.
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
to get the string as a value you can wrap this in with-out-str:
user=> (with-out-str (source nth))
"(defn nth\n \"Returns the value at the index. get returns nil if index out of\n bounds, nth throws an exception unless not-found is supplied. nth\n also works for strings, Java arrays, regex Matchers and Lists, and,\n in O(n) time, for sequences.\"\n {:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~#nf)))\n :inline-arities #{2 3}\n :added \"1.0\"}\n ([coll index] (. clojure.lang.RT (nth coll index)))\n ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))\n"
user=>
That was my message; nice to meet you ;-) BTW, the references given in that thread for answers were excellent reading; so if you're interested, you might want to take the time to read them. Back to your question though source seems to work for code that was loaded through a file, but it doesn't work in all cases. I think, specifically, it doesn't work for functions defined in the repl.
user=> (def foo (fn [] (+ 2 2)))
#'user/foo
user=> (source foo)
Source not found
nil
user=> (defn foo2 [] (+ 2 2))
#'user/foo2
user=> (source foo2)
Source not found
nil
Digging a little bit...
user=> (source source)
(defmacro source
"Prints the source code for the given symbol, if it can find it.
This requires that the symbol resolve to a Var defined in a
namespace for which the .clj is in the classpath.
Example: (source filter)"
[n]
`(println (or (source-fn '~n) (str "Source not found"))))
nil
user=> (source clojure.repl/source-fn)
(defn source-fn
"Returns a string of the source code for the given symbol, if it can
find it. This requires that the symbol resolve to a Var defined in
a namespace for which the .clj is in the classpath. Returns nil if
it can't find the source. For most REPL usage, 'source' is more
convenient.
Example: (source-fn 'filter)"
[x]
(when-let [v (resolve x)]
(when-let [filepath (:file (meta v))]
(when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
(with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
(dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
(let [text (StringBuilder.)
pbr (proxy [PushbackReader] [rdr]
(read [] (let [i (proxy-super read)]
(.append text (char i))
i)))]
(read (PushbackReader. pbr))
(str text)))))))
nil
So yeah, it looks like it tries to load the source file off the classpath to try to spit it out for you. One thing I've learned when working with Clojure is that 9 times out of 10 it is useful to look at the source.

Clojure recursive function

As a Clojure newbie, I'm bothered with this small problem:
I would like to iterate through a sequence and execute a split, and then a str (concatenation) function over the sequence elements.
Here is my sequence:
(("2.660.784") ("2.944.552") ("44.858.797"))
What I want to get is something like this:
("2660784" "2944552" "44858797")
And this is my attempt of creating recursive solution for my problem:
(defn old
[squence]
(let [size (count squence)]
(loop [counter 1]
(if (<= counter size)
(apply str (clojure.string/split
(first (first squence))
#"\b\.\b"
))
(old (rest squence)))
)))
And of course, this is not a solution because it applies split and str only to one element, but I would like to repeat this for each element in squence. The squence is product of some other function in my project.
I'm definitely missing something so please help me out with this one...
The simplest way to write it is with replace, rather than split/str. And once you've written a function that can do this transformation on a single string, you can use map or for to do it to a sequence of strings. Here I had to destructure a bit, since for whatever reason each element of your sequence is itself another sequence; I just pulled out the first element.
(for [[s] '(("2.660.784") ("2.944.552") ("44.858.797"))]
(clojure.string/replace s #"\b\.\b" ""))
user=> (defn reject-char-from-string
[ch sequence]
(map #(apply str (replace {ch nil} (first %))) sequence))
#'user/reject-char-from-string
user=> (reject-char-from-string \. '(("2.660.784") ("2.944.552") ("44.858.797"))
)
("2660784" "2944552" "44858797")
Tried this?
=> (flatten '(("2.660.784") ("2.944.552") ("44.858.797")))
("2.660.784" "2.944.552" "44.858.797")
Is it as simple as this?
(def data '(("2.660.784") ("2.944.552") ("44.858.797")))
(require '[clojure.string :as string])
(map #(string/replace (first %1) "." "") data)
;=> ("2660784" "2944552" "44858797")

"Unable to resolve symbol" error

When I paste this code into a REPL, it works fine:
(use 'clojure.contrib.seq-utils)
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
Now, I have this code with :gen-class :implements [my.ServiceInterface] and a function prefixed with - to implement the interface. I unit-test with Maven/Groovy/TestNG. Everything works fine with several other interfaces/Clojure implementations, but in this particular case, I get this error:
java.lang.RuntimeException:
java.lang.Exception: Unable to resolve symbol: random-letter in this context (NO_SOURCE_FILE:32)
I can't figure out why. The only thing I can tell is different in this function from all the other functions, is that this is the only place where I use quoting, i.e. '((random-digit)) and '(random-letter). EDIT: also, this is the only place where I use eval.
I tried defining the functions as "non-private" (defn instead of defn-). I also tried a (declare random-digit random-letter) at the top. Neither of these solves the problem.
On a side note, if you have a suggestion for a better way to implement the random-password function, I am all ears. But I'd still like to know why I am getting this error and how to get this to work.
Many thanks in advance for your help. Clojure is awesome.
Edit: here is the complete code.
(ns fred.hp2010.service.ClojurePoolerService
(:gen-class :implements [fred.hp2010.service.PoolerService])
(:use [clojure.contrib.seq-utils :only (flatten)]))
(def dao (fred.hp2010.persistence.Repository/getDao))
(declare find-by is-taken random-password)
(defn -addPooler [this pooler] (. dao insert "POOLER" pooler))
(defn -getPoolers [this] (. dao list "poolers"))
(defn -isEmailTaken [this email] (is-taken {"email" email}))
(defn -isUsernameTaken [this username] (is-taken {"username" username}))
(defn -login [this email password] (. dao findSingle "POOLER" {"email" email "password" password}))
(defn -changePassword [this email new-password]
(let [updated-pooler (assoc (into {} (find-by {"email" email})) "password" new-password)]
(. dao update "POOLER" "POOLER_ID" updated-pooler)))
(defn -resetPassword [this email]
(let [new-password (random-password)]
(-changePassword this email new-password)
new-password))
(defn- find-by [params] (. dao findSingle "POOLER" params))
(defn- is-taken [params] (not (nil? (find-by params))))
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
I don't know why you're having problems compiling this with :gen-class but I wouldn't be surprised if eval had something to do with it. eval is usually a bad idea. One thing to try (completely untested) is to use ` (backquote) instead of ' (quote) so that your symbols are namespace-qualified. Don't know if that'd help or not.
Probably better to get rid of eval though. If you turn your random-character functions into infinite lazy seqs via repeatedly you can do it this way:
(defn- random-letter [] (repeatedly #(char (+ (rand-int 26) 97))))
(defn- random-digit [] (repeatedly #(rand-int 10)))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(apply str
(mapcat (fn [[n f]] (take n (f)))
[[2 random-letter]
[1 random-digit]
[2 random-letter]
[1 random-digit]
[2 random-letter]])))
In the top handful of lines, I'm having a bit of trouble following the syntax. In particular, why all the quotes in line 7? Delayed evaluation of all those expressions is probably not helping you. I would guess that the quoted '(random-letter) is spoiling your fun.
You can probably write simpler code while eschewing eval. I'm going to go try it in the REPL, I hope to be back soon with an improved version.
EDIT:
OK, this works:
(apply str (interpose (random-digit) (repeat 3 (apply str (repeat 2 (random-letter))))))
...and it doesn't need anything from clojure.contrib :)
The str function will mung any arguments together into a string. If the arguments are in a list, you smuggle str inside the list by using apply.
As you said, Clojure is cool!
EDIT:
Here's a function that generates a random string of alphas and numerics in accordance with a String specification:
(apply str (map (fn [c] (if (= c \a) (random-letter) (random-digit))) "aanaanaa")))
It deviates a little bit from your spec but I think it's pretty cool.

Resources