I'm making a function that generates a .wav file. I have the header all set, but I'm running into trouble with the data itself. I have a function for creating a sine wave at 880Hz (at least I think that's what it does, but that's not my question)--the question is, how do I convert a collection of byte arrays to just one byte array with their contents? This was my best try:
(defn lil-endian-bytes [i]
(let [i (int i)]
(byte-array
(map #(.byteValue %)
[(bit-and i 0x000000ff)
(bit-shift-right (bit-and i 0x0000ff00) 8)
(bit-shift-right (bit-and i 0x00ff0000) 16)
(bit-shift-right (bit-and i 0xff000000) 24)]))))
(def leb lil-endian-bytes)
(let [wr (io/output-stream (str name ".wav") :append true)]
(.write wr
(byte-array (flatten (concat (map
(fn [sample] (leb (* 0xffff (math/sin (+ (* 2 3.14 880) sample)))))
(range (* duration s-rate)) )))))
but it doesn't do what I want it to do: concat all of the byte-arrays into one vector and then into a single byte array. And it makes sense to me why it can't: it can't concat/flatten a byte[] because it's not a vector; it's a byte[]. And it can't cast a byte[] into a byte. But what do I need to do to get this working?
You might be looking for something like:
(byte-array (mapcat seq my-sequence-of-byte-arrays))
Converting the byte arrays to be concatenated to sequences and back to byte array may be a little inefficient if you are dealing with large arrays. Here's how to concatenate byte arrays making java.nio.ByteBuffer do the heavy lifting:
(defn concat-byte-arrays [& byte-arrays]
(when (not-empty byte-arrays)
(let [total-size (reduce + (map count byte-arrays))
result (byte-array total-size)
bb (java.nio.ByteBuffer/wrap result)]
(doseq [ba byte-arrays]
(.put bb ba))
result)))
It will be as simple as:
Your byte arrays that needs to be combined in a single byte-array:
(def byte-arrays [(byte-array 10 (byte 1))
(byte-array 10 (byte 2))
(byte-array 10 (byte 3))])
Combine:
(byte-array (for [ar byte-arrays
i ar] i))
Related
I have about 20 short strings that I want to permutate. I want only permutations that have len == 8.
I would like to avoid calculating every possible permutation, as seen below:
import itertools
p = itertools.permutations([s1, s2, s3, s4, s5, s6,...])
for i in p:
s = ''.join(j for j in i)
if len(s)==8:
print(s)
But that's too slow right? How can I decrease the number of calculations? (to not spend processing and RAM).
The first, obvious thing to do is filter out any strings with length > 8:
newList = [i for i in [s1, s2, s3, s4, s5, s6, ...] if len(i) <= 8]
Then, you can use the second argument of itertools.permutations to set the number of items you want. If you have no empty strings in your list, you'll never need more than 8 items, so we can use 8 as the second argument:
p = itertools.permutations(newList, 8)
However, if any of your strings are longer than one character, this won't get you what you want, since it will only return permutations of exactly 8 items. One way to resolve this is to iterate through the various lengths:
pList = [itertools.permutations(newList, length) for length in range(1, 9)]
Yet here you end up with an enormous amount of permutations to filter through: P(20, 8) + P(20, 7) + ... P(20, 1) = roughly 5.5 billion, which is impractical to work with.
A different direction
Instead of using permutations, let's use combinations, of which there are far fewer ("only" 263,949). Recall that in combinations, the order of the combined items doesn't matter, while in permutations it does. Thus we can use the smaller set of combinations to filter for the length 8 that we want:
cList = (combo for length in range(1, 9)
for combo in itertools.combinations(newList, length)
if len(''.join(combo)) == 8)
Using () instead of [] will make this a generator rather than a list, to delay evaluation until we really need it. And now we are close!
We can get our final result by taking the permutations of the items in cList:
result = [''.join(perm) for combo in cList
for perm in itertools.permutations(combo)]
I'm a beginner in Emacs lisp, so this is really a noob question. Let's say that I have to write a function that uses a loop to add 1 to each element of a numeric vector.
Here is the code I wrote (the comments indicate what I'm trying to do at each step):
(defun add-one (x)
"Use a loop to add 1 to each element of list X"
(let* ((res x) ; make a copy of X
(counter 0)) ; set counter to 0
(while (< counter (length x))
;; Replace each element of RES by adding 1:
(setcar (nthcdr counter res) (1+ (car (nthcdr counter x))))
(setq counter (1+ counter)))
;; Display result:
(message "%s" res)))
But my code seems to be destructive for x, since several calls to the function do not produce the same result:
;; Define a list:
(setq mylist '(1 2 3 4))
;; Several calls to the function:
(add-one mylist) ; -> (2 3 4 5)
(add-one mylist) ; -> (3 4 5 6)
Here is my question: I don't understand why my code is destructive (I expected the result to be (2 3 4 5) at each execution). I know that setcar is destructive, but it is applied to a copy of x, not to x itself. So why is the result changing?
Thanks!
For the sake of clarity:
Despite the fact you do not copy input list, your code is not at all lispy. Try this instead:
(mapcar '1+ '(1 2 3 4))
Let does not make a copy of anything, so this assigns the value referenced by the variable x to the variable res. Hence any changes to the list referenced by res also change the list referenced by x
(let* ((res x) ; make a copy of X
Being new with CL, I play a lot with simple algorithms. For instance, I tried to implement a function for removing all unique elements in a list.
(1 2 2 3 3 4 5 3) -> (2 2 3 3 3)
First attempt lead to this code:
(defun remove-unique (items)
(let ((duplicates (set-difference items (remove-duplicates items :test #'equal))))
(append duplicates (remove-duplicates duplicates :test #'equal))))
This works ok with strings but does always return NIL for numbers. Reading a bit more about set-difference I've learned that it isn't suppose to work with duplicate populated lists at all, it just works somehow in my case, so I abandoned the approach as unportable and moved along.
Another attempt is:
(defun remove-unique (items)
(loop for item in items
when (member item (cdr (member item items)))
collect item))
And this works ok with numbers, but returns NIL for strings.
Apparently there is a core difference between strings and numbers I don't understand. How come list processing functions such as member and set-difference work differently on them?
The equality comparison for numbers, characters and strings is indeed different. Equal, which you should be wary to use because it is more expensive, does structure equality (so it descends on some objects). eq does object equality. And eql does object equality for most cases except for numbers (where they check type and value) and characters (where they check 'value')
See the hyperspec entries for equal, eql and eq for more information.
Strings are more related to lists than numbers since both lists and strings are sequences.
"Hello" is a sequence (compund data type) starting with the primitive character value #\H and ending with #\o.
'(1 2 3) is a sequence (compond data type) starting with the primitive numeric value 1 and ending with 3.
Characters are similar to numbers in that they are primitive values. Primitive values can be compared using eql while sequences, that are not the same object, can be compared using equal
(setq list1 (list 1 2 3))
(setq list2 (list 1 2 3))
(eql list1 list2)
;==> NIL
(equal list1 list2)
;==> T
;; comparing first element of both lists using eql
(eql (car list1) (car list2))
;==> T
(setq string1 "Hello")
(setq string2 "Hello")
(eql string1 string2)
;==> NIL
(equal string1 string2)
;==> T
;; comparing first character of both strings using eql
(eql (elt string1 0) (elt string2 0))
;==> T
Most (if not all) functions in Common Lisp that compares something usually has an optional named argument :test where you can supply how elements compare. the default usually is eql. To make them behave corretly with sequences you need to supply #'equal as :test.
(defun remove-unique (items &key (test 'eql))
(loop
:with table := (make-hash-table :test test)
:for element :in items :do
(setf (gethash element table)
(1+ (gethash element table 0)))
:finally
(return
(loop
:for k :being :the :hash-keys :of table
:using (:hash-value v)
:when (> v 1) :nconc (make-list v :initial-element k)))))
(defun remove-unique (items &key (test 'eql))
(loop
:with table := (make-hash-table :test test)
:for element :in items :do
(setf (gethash element table)
(1+ (gethash element table 0)))
:finally
(return
(loop
:for element :in items
:unless (= 1 (gethash element table))
:collect element))))
I'd probably use the first variant because it makes less reads from hash-table, but you'd need to check that items in the list aren't modified later in place.
(remove-unique '("1" "2" "2" "3" "3" "4" "5" "3") :test #'equal)
gives:
("2" "2" "3" "3" "3")
but
(remove-unique '("1" "2" "2" "3" "3" "4" "5" "3"))
gives:
NIL
I'll start with my test using the speclj framework.
(it "turns the string into a hash-map"
(should= {1 "1" 2 "2" 3 "3"}
(format-string "1=1 2=2 3=3")))
Then my code:
(:use [clojure.string :only (split)])
(defn format-string [string]
(split string #"\s+"))
Right now, the format-string function returns ["1=1" "2=2" "3=3"] and the test fails. As you can see in my test, I want it to return a hash-map with key-value pairs indicated by the = sign.
I've tried a few things and I've gotten close, but can't quite understand how to make this transformation.
EDIT
Figured out one solution, although the keys are strings instead of integers.
My code:
(defn format-board [route]
(let [[first second third] (split route #"\s+")]
(merge
(apply hash-map (split-at-equals first))
(apply hash-map (split-at-equals second))
(apply hash-map (split-at-equals third))
This returns {"1" "1" "2" "2" "3" "3"}.
You have split at the spaces, but then you need to split again at the = delimiter. You can use regular expressions to do the parsing. Once you have your pairs you can assoc into a hash-map. Here I've used reduce to effect the transformation.
user=> (reduce #(assoc % (read-string (nth %2 1)) (nth %2 2)) {}
#_> (re-seq #"([^=\s]+)=([^=\s]+)" "1=1 2=2 3=3") )
{3 "3", 2 "2", 1 "1"}
Note key order is not applicable to hash-maps
user=> (= {1 "1", 2 "2", 3 "3"} *1)
true
Here is the potential parallel version using clojure.core.reducers:
(require '[clojure.core.reducers :as r])
(require '[clojure.string :as s])
(def string-of-pairs "1=1 2=2 3=3 4=4")
; reducing fn to convert seq of (key, value) to hash-map
(defn rf ([] {}) ([acc [k v]] (assoc acc k v)))
; for large colls, fold will parallelize execution
(r/fold merge rf (r/map #(s/split % #"=") (s/split string-of-pairs #"\s+")))
For better understanding of reducers, watch this video where
Rich explains motivation behind reducers and demonstrates some usage.
As records are immutable i am unable to read in the data and parse it without it making a new instance of itself. Moreover, how would i be able to read my excel files from a number of specific columns instead of reading from column 0 to EOF. Is there anyway where i could read in the data from say column 1, column 3, column 5. Supposedly, Column 1 would be parsed as a string, Column 3 as an integer and Column 5 as a long.
(defrecord Record [Name Age Index])
(defn read-csv [fname count]
(with-open [file (io/reader fname)]
(doall (take count (map (comp first csv/read-csv)
(line-seq file))))))
(def records (map #(apply ->Record %) (read-csv "C:/Users/user/Documents/URECA/hi/lib/test.csv" 1)))
This is what i have but it seems to read the columns incrementally
To keep quotes of text fields you can parse csv file by regexp:
(defn read-csv [fname count]
(with-open [file (io/reader fname)]
(doall (map #(str/split % #",") ; doesn't work with commas in text fields
(take count (line-seq file))))))
(defn make-record [idxs types row]
(apply ->Record
(map (fn [idx t]
(let [value (nth row idx)]
(case t
:string value
:int (Integer/parseInt value)
:long (Long/parseLong value))))
idxs types)))
(def records (map (partial make-record
[0 2 4]
[:string :int :long])
(read-csv "/home/mobyte/test.csv" 3)))
(pprint records)
-> ({:Name "\"s1\"", :Age 1, :Index 111}
{:Name "\"s2\"", :Age 2, :Index 112}
{:Name "\"s3\"", :Age 3, :Index 113})
(type (:Age (first records)))
->java.lang.Integer
(type (:Index (first records)))
-> java.lang.Long
(type (:Name (first records)))
-> java.lang.String