I'm having a problem where variables are being interpreted as a string rather than the intended value.
I'm trying to append pairs of numbers to a list, grouped by curlys, but it doesn't work with variable substitution.
set new_list {}
lappend new_list {4 5}
lappend new_list {7 8}
puts "$list"
Output:
{4 5} {7 8}
This is the desired format.
However when I try the following, let's say for instance I wanted all integers to 10 and their squared value to be appended to the list as pairs:
for {set i 0} {$i < 10} {incr i} {
lappend new_list {$i [expr pow($i, 2)]}
}
Output:
{$i [expr pow($i, 2)]} {$i [expr pow($i, 2)]} {$i [expr pow($i, 2)]} {$i [expr pow($i, 2)]} .... and so on ....
I'd want the values as integer pairs: {1 1} {2 4} {3 9} ...
Any help in achieving this?
You have to evaluate the words (expressions, etc) at the time that you do the lappend. To do that, you build the sublist to be appended with the list construction command:
for {set i 0} {$i < 10} {incr i} {
lappend new_list [list $i [expr {$i ** 2}]]
}
Braces by themselves mean “do not expand anything in this now”. (That's their actual meaning in Tcl. It's just that some commands, such as for, then go right ahead and do their thing with what's inside them immediately.) You could, in this case, have put the things to insert in double quotes, but that's bad practice as it can bite you hard once you start using real-world data (such as people's surnames with spaces in); the list command is specifically designed (and thoroughly tested) to have no such weaknesses.
In this specific case, I'd write $i ** 2 instead of pow($i, 2) because the latter always produces a floating point result, whereas the former is sensitive to numeric types (just like other expr operators).
Related
Assum I hava some(more than 20) variables, I want to save them to a file. I don't want to repeat 20 times the same code.
I wrote a macro but it gave me an error.
my test case:
;-----------------------------------------------
(defn processor [ some-parameters ]
(let [
;after some operation ,got these data:
date-str ["JN01","JN02","JN03","JN04"];length 8760
date-temperature (map #(str %2 "," %1) [3.3,4.4,5.5,6.6] date-str) ; all vector's length are 8760
date-ws (map #(str %2 "," %1) [0.2,0.1,0.3,0.4] date-str) ;
;... many variables such like date-relative-humidity,date-pressure, name starts with "date-",
; all same size
]
;(doseq [e date-temperature]
; (println e))
(spit "output-variable_a.TXT"
(with-out-str
(doseq [e date-temperature]
(println e))))
;same 'spit' part will repeat many times
))
(processor 123)
; I NEED to output other variables(ws, wd, relative-humidity, ...)
; Output example:
;JN01,3.3
;JN02,4.4
;JN03,5.5
;JN04,6.6
;-----------------------------------------------
what I want is a macro/function I can use this way:
(write-to-text temperature,ws,wd,pressure,theta-in-k,mixradio)
and this macro/function will do the work.
I don't know how to write such a macro/function.
My macro post here but it doesn't work:
(defmacro write-array [& rest-variables ]
`(doseq [ vname# '~rest-variables ]
;(println vname# vvalue#)
(println "the vname# is" (symbol vname#))
(println "resolve:" (resolve (symbol (str vname# "-lines"))))
(println "resolve2:" (resolve (symbol (str "ws-lines"))))
(let [ vvalue# 5] ;(var-get (resolve (symbol vname#)))]
;----------NOTE: commented out cause '(symbol vname#)' won't work.
;1(spit (str "OUT-" vname# ".TXT" )
;1 (with-out-str
;1 (doseq [ l (var-get (resolve (symbol (str vname# "-lines"))))]
;1 (println l))))
(println vname# vvalue#))))
I found that the problem is (symbol vname#) part, this method only works for a GLOBAL variable, cannot bound to date-temperature in the LET form,(symbol vname#) returns nil.
It looks like you want to write a file of delimited values using binding names and their values from inside a let. Macros transform code during compilation and so they cannot know the run-time values that the symbols you pass are bound to. You can use a macro to emit code that will be evaluated at run-time:
(defmacro to-rows [& args]
(let [names (mapv name args)]
`(cons ~names (map vector ~#args))))
(defn get-stuff []
(let [nums [1 2 3]
chars [\a \b \c]
bools [true false nil]]
(to-rows nums chars bools)))
(get-stuff)
=> (["nums" "chars" "bools"]
[1 \a true]
[2 \b false]
[3 \c nil])
Alternatively you could produce a hash map per row:
(defmacro to-rows [& args]
(let [names (mapv name args)]
`(map (fn [& vs#] (zipmap ~names vs#)) ~#args)))
=> ({"nums" 1, "chars" \a, "bools" true}
{"nums" 2, "chars" \b, "bools" false}
{"nums" 3, "chars" \c, "bools" nil})
You would then need to write that out to a file, either using data.csv or similar code.
To see what to-rows expands to, you can use macroexpand. This is the code being generated at compile-time that will be evaluated at run-time. It does the work of getting the symbol names at compile-time, but emits code that will work on their bound values at run-time.
(macroexpand '(to-rows x y z))
=> (clojure.core/cons ["x" "y" "z"] (clojure.core/map clojure.core/vector x y z))
As an aside, I'm assuming you aren't typing thousands of literal values into let bindings. I think this answers the question as asked but there could likely be a more direct approach than this.
I think you are looking for the function name. To demonstrate:
user=> (defmacro write-columns [& columns]
(let [names (map name columns)]
`(str ~#names)))
#'user/write-columns
user=> (write-columns a b c)
"abc"
You can first capture the variable names and their values into a map:
(defmacro name-map
[& xs]
(let [args-list# (cons 'list (map (juxt (comp keyword str) identity) xs))]
`(into {} ~args-list#)))
If you pass the var names to the macro,
(let [aa 11
bb 22
cc 33]
(name-map aa bb cc))
It gives you a map which you can then use for any further processing:
=> {:aa 11, :bb 22, :cc 33}
(def result *1)
(run!
(fn [[k v]] (println (str "spit file_" (name k) " value: " v)))
result)
=>
spit file_aa value: 11
spit file_bb value: 22
spit file_cc value: 33
Edit: Just noticed it's similar to Taylor's macro. The difference is this one works with primitive types as well, while Taylor's works for the original data (vars resolving to collections).
I supposed, the result should be 1, 2, 3.
> my ($a, $b, $c)
> (($a, $b), $c) = ((1, 2), 3)
(((1 2) 3) (Any))
> $a, $b, $c
((1 2) 3 (Any))
What's wrong here?
There's nothing wrong (that is, ordinary assignment in P6 is designed to do as it has done) but at a guess you were hoping that making the structure on the two sides the same would result in $a getting 1, $b getting 2 and $c getting 3.
For that, you want "binding assignment" (aka just "binding"), not ordinary assignment:
my ($a, $b, $c);
:(($a, $b), $c) := ((1, 2), 3);
Note the colon before the list on the left, making it a signature literal, and the colon before the =, making it a binding operation.
If you want to have the result be 1, 2, 3, you must Slip the list:
my ($a, $b, $c) = |(1, 2), 3;
This is a consequence of the single argument rule: https://docs.raku.org/type/Signature#Single_Argument_Rule_Slurpy
This is also why this just works:
my ($a, $b, $c) = (1, 2, 3);
Even though (1,2,3) is a List with 3 elements, it will be auto-slipped because of the same single argument rule. You can of course also just remove the (superstitious) parentheses:
my ($a, $b, $c) = 1, 2, 3;
You are asking *What's wrong here", and I would say some variant of the single argument rule is at work. Since parentheses are only used here for grouping, what's going on is this assignment
($a, $b), $c = (1, 2), 3
(1, 2), 3 are behaving as a single argument, so they are slurpily assigned to the first element in your group, $a, $b. Thus they get it all, and por old $c only gets Any. Look at it this way:
my ($a, $b, $c);
($a, ($b, $c)) = ((1, 2), 3, 'þ');
say $a, $c; # OUTPUT: «(1 2)þ»
You might want to look at this code by Larry Wall, which uses »=«, which does exactly what you are looking for. It's not documented, so you might want to wait a bit until it is.
I am learning clojure at school and I have an exam coming up. I was just working on a few things to make sure I get the hang of it.
I am trying to read from a file line by line and as I do, I want to split the line whenever there is a ";".
Here is my code so far
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil/Documents/work.txt.txt")]
(doseq [line (line-seq rdr)]
(clojure.string/split line #";")
(println line)))))
When I do this, I still get the output:
"I;Am;A;String;"
Am I missing something?
I'm not sure if you need this at school, but since Gary already gave an excellent answer, consider this as a bonus.
You can do elegant transformations on lines of text with transducers. The ingredient you need is something that allows you to treat the lines as a reducible collection and which closes the reader when you're done reducing:
(defn lines-reducible [^BufferedReader rdr]
(reify clojure.lang.IReduceInit
(reduce [this f init]
(try
(loop [state init]
(if (reduced? state)
#state
(if-let [line (.readLine rdr)]
(recur (f state line))
state)))
(finally
(.close rdr))))))
Now you're able to do the following, given input work.txt:
I;am;a;string
Next;line;please
Count the length of each 'split'
(require '[clojure.string :as str])
(require '[clojure.java.io :as io])
(into []
(comp
(mapcat #(str/split % #";"))
(map count))
(lines-reducible (io/reader "/tmp/work.txt")))
;;=> [1 2 1 6 4 4 6]
Sum the length of all 'splits'
(transduce
(comp
(mapcat #(str/split % #";"))
(map count))
+
(lines-reducible (io/reader "/tmp/work.txt")))
;;=> 24
Sum the length of all words until we find a word that is longer than 5
(transduce
(comp
(mapcat #(str/split % #";"))
(map count))
(fn
([] 0)
([sum] sum)
([sum l]
(if (> l 5)
(reduced sum)
(+ sum l))))
(lines-reducible (io/reader "/tmp/work.txt")))
or with take-while:
(transduce
(comp
(mapcat #(str/split % #";"))
(map count)
(take-while #(> 5 %)))
+
(lines-reducible (io/reader "/tmp/work.txt")))
Read https://tech.grammarly.com/blog/building-etl-pipelines-with-clojure for more details.
TL;DR embrace the REPL and embrace immutability
Your question was "what am I missing?" and to that I'd say you're missing one of the best features of Clojure, the REPL.
Edit: you might also be missing that Clojure uses immutable data structures so
consider this code snippet:
(doseq [x [1 2 3]]
(inc x)
(prn x))
This code does not print "2 3 4"
it prints "1 2 3" because x isn't a mutable variable.
During the first iteration (inc x) gets called, returns 2, and that gets thrown away because it wasn't passed to anything, then (prn x) prints the value of x which is still 1.
Now consider this code snippet:
(doseq [x [1 2 3]] (prn (inc x)))
During the first iteration the inc passes its return value to prn so you get 2
Long example:
I don't want to rob you of the opportunity to solve the problem yourself so I'll use a different problem as an example.
Given the file "birds.txt"
with the data "1chicken\n 2duck\n 3Larry"
you want to write a function that takes a file and returns a sequence of bird names
Lets break this problem down into smaller chunks:
first lets read the file and split it up into lines
(slurp "birds.txt") will give us the whole file a string
clojure.string/split-lines will give us a collection with each line as an element in the collection
(clojure.string/split-lines (slurp "birds.txt")) gets us ["1chicken" "2duck" "3Larry"]
At this point we could map some function over that collection to strip out the number like (map #(clojure.string/replace % #"\d" "") birds-collection)
or we could just move that step up the pipeline when the whole file is one string.
Now that we have all of our pieces we can put them together in a functional pipeline where the result of one piece feeds into the next
In Clojure there is a nice macro to make this more readable, the -> macro
It takes the result of one computation and injects it as the first argument to the next
so our pipeline looks like this:
(-> "C:/birds.txt"
slurp
(clojure.string/replace #"\d" "")
clojure.string/split-lines)
last note on style, for Clojure functions you want to stick to kebab case so readFile should be read-file
I would keep it simple, and code it like this:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]
[clojure.string :as str] ))
(def text
"I;am;a;line;
This;is;another;one
Followed;by;this;")
(def tmp-file-name "/tmp/lines.txt")
(dotest
(spit tmp-file-name text) ; write it to a tmp file
(let [lines (str/split-lines (slurp tmp-file-name))
result (for [line lines]
(for [word (str/split line #";")]
(str/trim word)))
result-flat (flatten result)]
(is= result
[["I" "am" "a" "line"]
["This" "is" "another" "one"]
["Followed" "by" "this"]])
Notice that result is a doubly-nested (2D) matrix of words. The simplest way to undo this is the flatten function to produce result-flat:
(is= result-flat
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
You could also use apply concat as in:
(is= (apply concat result) result-flat)
If you want to avoid building up a 2D matrix in the first place, you can use a generator function (a la Python) via lazy-gen and yield from the Tupelo library:
(dotest
(spit tmp-file-name text) ; write it to a tmp file
(let [lines (str/split-lines (slurp tmp-file-name))
result (t/lazy-gen
(doseq [line lines]
(let [words (str/split line #";")]
(doseq [word words]
(t/yield (str/trim word))))))]
(is= result
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
In this case, lazy-gen creates the generator function.
Notice that for has been replaced with doseq, and the yield function places each word into the output lazy sequence.
I'm trying to get a pointer to a double value within a struct that I can pass to a routine, but having trouble getting any value other than 0. The following has worked for other struct! values I've used (such as one containing only binary!), but not in this case:
Rebol []
speed1: make struct! [
d [double]
] [0.0]
speed1*-struct: make struct! [i [int]] third speed1
speed1*: speed1*-struct/i
Here is the evaluation:
>> speed1: make struct! [
[ d [double]
[ ] [0.0]
>> speed1*-struct: make struct! [i [int]] third speed1
>> speed1*: speed1*-struct/i
== 0
Here is the working equivalent with a binary! struct:
>> binary: make struct! [bytes [binary!]] reduce [head insert/dup copy #{} 0 8]
>> binary*-struct: make struct! [i [int]] third binary
>> binary*: binary*-struct/i
== 39654648
I can see the difference in the third function:
>> third speed1
== #{0000000000000840}
>> third binary
== #{F8145D02}
However, I am not really sure what the difference in length means in this case. What am I doing wrong? Is there a different way to pass pointers to a decimal value?
Here is a way to get a pointer to a double value:
speed1-struct: make struct! [s [struct! [d [double]]]] [0.0]
speed1*-struct: make struct! [i [int]] third speed1-struct
speed1*: speed1*-struct/i
And the evaluation showing it returns a pointer (shown as int):
>> speed1-struct: make struct! [s [struct! [d [double]]]] [0.0]
>> speed1*-struct: make struct! [i [int]] third speed1-struct
>> speed1*: speed1*-struct/i
== 37329176
Say there is a code like this:
set val "Hello"
set listA {}
lappend listA 6 7
Now I want to puts the following:
puts "${val} user! Your list contains two values. First is [lindex $listA 0] and the second is [lindex $listA 1]"
How I can do this?
I'm not fully sure if I understood correctly. But what you did should work, with a small modification:
set val "Hello"
set listA {6 7}
# or:
# set listA {}
# lappend listA 6 7
puts "${val} user! Your list contains two values. First is [lindex $listA 0] and the second is [lindex $listA 1]"
Gives the output:
Hello user! Your list contains two values. First is 6 and the second is 7
Ok, I have found the answer. The problem was in the fact that in my actual code I was using "[" and "]" symbols as a string but without "\".
So I need to write:
puts "Zone No ${key} has Range\[ [lindex $value 0] - [lindex $value 1] \]"
Sorry for the question.