Lilypond: How can I map numbers to notes with Scheme - lilypond

I'm trying to create a function where, for an example, \generateNote #3 #4 would generate an f4 - this would help to write functions to quickly generate scales etc.
generateNote =
#(define-music-function
(parser location note)
(number?)
(define notes
'((0 . "c4")
(1 . "d4")
(2 . "e4")
(3 . "f4")
(4 . "g4")
(5 . "a4")
(6 . "b4")))
#{ #(cdr (assoc (modulo note 7) notes)) #}
)
This does not work because error: music function cannot generate f4. The following, however, does work:
generateF =
#(define-music-function
(parser location)
#{ f4 #}
)
Any thoughts about why this isn't working?
I already tried swapping the " " with { } and #{ #} to no avail.

I managed to get something going with
generateNote =
#(define-music-function
(parser location note duration)
(number? number?)
(define notes
'((0 . "c")
(1 . "d")
(2 . "e")
(3 . "f")
(4 . "g")
(5 . "a")
(6 . "b")))
#{
$(ly:parser-include-string
parser
(string-append
(cdr (assoc (modulo note 7) notes))
(number->string duration)
)
)
#}
)
It at least works.

Related

How do I serialize an object into JSON including defined variables in Racket?

I am trying to to write a json structure to a file using racket. The actual function for writing works fine but when creating the data structure which I write to the file, I simply cannot figure out how to include values from bound variables.
(start_location . "h")
(end_location . "t")
(ETA . 30)
(route . ("g"))
(optional_parameters . #hasheq((accessibility . #f)))
))
(define write-json-wrapper (lambda (jsexpr filename)
(call-with-output-file filename (lambda (x) (write-json jsexpr x)) #:exists 'replace)
))
The above structure works when I write it to the file.
(define x "h")
(define b #hasheq(
(start_location . x)
(end_location . "t")
(ETA . 30)
(route . ("g"))
(optional_parameters . #hasheq((accessibility . #f)))
))```
However when I try to use the value stored in x as the value for start_location it throws an error.
write-json: expected argument of type <legal JSON value>; given: 'x
Any help or explanation would be much appreciated.
#hasheq is literal for hash table and its content isn't evaluated:
#hasheq((a . 5) (b . 7)) reads equal to (make-...eq '((b . 7) (a . 5)))
If you want to use values of variables, you have to use make-hasheq like this:
#lang racket
(require json)
(define x 1)
(define y 2)
(define z 3)
(define jsexpr
(make-hasheq (list (cons 'x x)
(cons 'y y)
(cons 'z z))))
(define write-json-wrapper (lambda (jsexpr filename)
(call-with-output-file filename
(lambda (x) (write-json jsexpr x)) #:exists 'replace)))
Example:
(write-json-wrapper jsexpr "foo.txt")
If you're familiar with quasiquote, you can do also this:
(define jsexpr
(make-hasheq `((x . ,x)
(y . ,y)
(z . ,z))))
So for your data:
(make-hasheq `((start_location . ,x)
(end_location . "t")
(ETA . 30)
(route . ("g"))
(optional_parameters . #hasheq((accessibility . #f)))))

Turning DB information into JSON with keys?

I have a database that returns data as a tree like this:
'((7 "vince" "vince1#test.com" "space" "no value" 1)
(8 "vince" "vince2#test.com" "place" "no value" 1)
(9 "Smith" "Smith#gmail.com" "now" "no value" 1))
The second column is first name and the third column is email.
My goal is to return JSON key value pairs but im struggling
Here is what I have tried:
Function to get name and email from one list item
(defun get-name-&-emails-db1 (lst)
(if (null lst)
nil
(let* ((name (second lst))
(email (third lst)))
(cl-json:encode-json-to-string `((:name . ,name)(:email . ,email))))))
Map over data set
(mapcar #'get-name-&-emails-db1 (return-data-tree))
This returns a list of individual json blocks. But I want it to be ONE json block with all records.
What am I missing?
(ideally, I want to know how to do this without any additional libraries)
Thanks
I tried to encode a list of alists, and this is how it goes:
USER> (cl-json:encode-json
(list '(("a" . "0") ("b" . "1")) '(("a" . "2") ("b" . "3"))))
[{"a":"0","b":"1"},{"a":"2","b":"3"}]
If this is what you want to have, then you need to organize your data in Lisp first, then encode the whole list as JSON instead of formatting each entry individually.
Use mapcar, get the second and third element of each entry, and then call cl-json:encode-json-to-string on the result:
(let ((data '((7 "vince" "vince1#test.com" "space" "no value" 1)
(8 "vince" "vince2#test.com" "place" "no value" 1)
(9 "Smith" "Smith#gmail.com" "now" "no value" 1))))
(cl-json:encode-json-to-string
(mapcar (lambda (e) `((:name . ,(second e))(:email . ,(third e))))
data)))
Here I don't use comma, backquote, alists or plists, but simply: I create a list of hash-tables. I'm quite sure how a list and a hash table are rendered in JSON, so let's rework our data a bit to come back in known territories.
(loop for row in '((7 "vince" "vince1#test.com" "space" "no value" 1)
(8 "vince" "vince2#test.com" "place" "no value" 1)
(9 "Smith" "Smith#gmail.com" "now" "no value" 1))
with result = (list) ;; the list of hash-tables to encode.
for ht = (make-hash-table) ;; intermediary hash-table.
do (setf (gethash "name" ht)
(second row)
(gethash "email" ht)
(third row))
(push ht result)
finally (return (cl-json:encode-json-to-string result)))
;; =>
"[{\"name\":\"Smith\",\"email\":\"Smith#gmail.com\"},{\"name\":\"vince\",\"email\":\"vince2#test.com\"},{\"name\":\"vince\",\"email\":\"vince1#test.com\"}]"
I like Serapeum's dict:
;; replaces (for ht = (make-hash-table)) and the setf
for ht = (dict :name (second row)
:email (third row))
Answers were given. Just a general way to deal with alist and json:
(ql:quickload :yason)
(defparameter *data* '((7 "vince" "vince1#test.com" "space" "no value" 1)
(8 "vince" "vince2#test.com" "place" "no value" 1)
(9 "Smith" "Smith#gmail.com" "now" "no value" 1)))
(defparameter *scheme* '(:id :name :email :meta :value :count))
(defun pairing (keys values)
(loop for a in keys
for b in values
collect (cons (string-downcase (format nil "~A" a)) b)))
(defun alist-json (x &keys keys)
(with-output-to-string (*standard-output*)
(yason:encode-alist (pairing keys x))))
(defun list-json (l)
(format nil "[~{~A~^, ~}]" l))
(defun values-list-keys-json (values-list keys)
(list-json (mapcar (lambda (x) (alist-json x :keys keys)) values-list)))
(values-list-keys-json *data* *scheme*)
#|
=> "[{\"id\":7,\"name\":\"vince\",\"email\":\"vince1#test.com\",\"meta\":\"space\",\"value\":\"no value\",\"count\":1},
{\"id\":8,\"name\":\"vince\",\"email\":\"vince2#test.com\",\"meta\":\"place\",\"value\":\"no value\",\"count\":1},
{\"id\":9,\"name\":\"Smith\",\"email\":\"Smith#gmail.com\",\"meta\":\"now\",\"value\":\"no value\",\"count\":1}]"
|#
(defun second-third (l)
(subseq l 1 3))
(values-list-keys-json (mapcar #'second-third *data*) (second-third *scheme*))
#|
=> "[{\"name\":\"vince\",\"email\":\"vince1#test.com\"}, {\"name\":\"vince\",\"email\":\"vince2#test.com\"}, {\"name\":\"Smith\",\"email\":\"Smith#gmail.com\"}]"
|#

map 2 function combination fails: syntax error?

I just know in Haskell "." could be used to combine functions, so I tried:
Prelude> map (++" world")["hello","abc"]
["hello world","abc world"]
Prelude> map (++" world". ++"xyz")["hello","abc"]
<interactive>:3:18: parse error on input `++'
Why I cannot do this? I tried named function, it's OK:
Prelude> map (head.tail)["hello","abc"]
"eb"
So how to correct my case? Thanks.
The name for the thing you are doing when you say (++ "world") is that you are using what is called a "right section" of a "partially applied infix operator":
https://wiki.haskell.org/Section_of_an_infix_operator
What that means for (++ "world") is that the left hand side will be the parameter for the generated function, which will pre-fill the right hand side with "world".
So it's not just a use of parentheses for precedence... it's a special syntax. When you omit the parentheses on the second ++, that means you aren't invoking the syntax there. It tries to interpret it as normal infix.
If you want to compose two functions of this type inlined like this, you have them each in parentheses:
Prelude> map ((++" world") . (++"xyz"))["hello","abc"]
["helloxyz world","abcxyz world"]
Note the effects on the result if you merely convert the ++ operators to prefix, with the non-partially-applied syntax:
Prelude> map ((++) " world" . (++) "xyz")["hello","abc"]
[" worldxyzhello"," worldxyzabc"]
There you don't have to group them, but now you are providing the first argument instead. Your list's data winds up at the end of the output. This would be the same as if you used the infix partial application syntax with left sections:
Prelude> map ((" world" ++) . ("xyz" ++))["hello","abc"]
[" worldxyzhello"," worldxyzabc"]
FYI: if you want the composition to be in the reverse order (world first, then xyz) you could use >>> from Control.Arrow
Prelude> import Control.Arrow
Prelude Control.Arrow> map ((++" world") >>> (++"xyz")["hello","abc"]
["hello worldxyz","abc worldxyz"]
++ is an infix operator, and the rules of how you can use it are different than that of normal functions.
You can use it sandwiched between two values, like this
x ++ y
or convert it to a normal function using parenthesis
(++) x y --same as above
There are two ways to partially apply values to an infix operator
(x ++) --same as \y -> x ++ y
(++ y) --same as \x -> x ++ y
Both of these require the outer parentheses, else Haskell would try to parse the expression as the normal infix case
++ x . ++ y -- will confuse the parser
but
(++ x) . (++ y)
will work
Infix operators like parentheses.
Prelude> map ((++ " world") . (++ "xyz")) ["hello", "abc"]

Run a 3-arg (etc) function against a sequence of inputs

I'm trying to write some clojure which builds up a data-structure that will be valid clojure code as well. (note I'm not building a macro here: just a function that happens to return clojure code).
Here's my working bit:
(defn create-clause [ property operator value ]
(list (symbol operator) (symbol property) (symbol value))
)
(create-clause "b" "<" "5")
So this creates a 'clause' like this:
(< b 5)
And this works.
What I want to do is end up with something like this:
(and (= AccountType "current") (< Balance 0))
I can chain up a couple of clauses manually like this:
(list 'and (create-clause "a" "=" "current") (create-clause "b" "<" "0"))
Which results in:
(and (= a "current") (< b 0))
But I want a function that takes my 3-string arguments "property" "operator" "value" , creates the clause and results in a combined 'and' list of all the clauses which can be evaluated (assume the symbols are bound in a 'let' of course...)
EDIT: Got a bit closer but still no cigar....
Slightly refactored the func - so it now takes a single argument of a list , rather than 3 separate arguments:
(defn create-clause [ [ property operator value ] ]
(list (symbol operator) (symbol property) (symbol value))
)
(create-clause [ "b" "<" "5" ] )
Now using a loop/recur - and I can nearly get what I need:
(list 'and (loop [ input [ "a" "<" "10" , "b" "<" "5" ] output [] ]
(if (= (count input) 0) output
(recur (drop 3 input) (conj output (create-clause(take 3 input)))))))
The result of the above is:
(and [(< a 10) (< b 5)]) ; wrong - don't want that internal vector wrapper...
EDIT #2:
So I'm thinking I have accumulate the result somewhere in my loop - so I might was well stick to using the vector as above (but dropping the (list 'and...) ) - but then I should be able to apply something to the result of it which will have the effect of turning this something like this structure:
[ (= 1 1) (= 1 1) ]
Into this structure:
(and (= 1 1) (= 1 1))
But now I'm stuck again.....
EDIT #3:
Ok , I got there - I have ended up with an unholy mess - but at least it works and I can start refactoring now !
(conj (seq (loop [ input [ "AccountType" "=" "current" , "Balance" "<" 5 ] output [] ]
(if (= (count input) 0) output
(recur (drop 3 input) (conj output (create-clause(take 3 input))))))) 'and)
(and (= AccountType "current") (< Balance 5))
partition will chunk up a sequence into a certain size:
(def s (partition 3 (range 6)))
; s => ((0 1 2) (3 4 5))
A combination of map, partial and apply will let you call your method:
(defn reorder [a b c] (list b c a))
(def reordered (map (partial apply reorder) s))
; reordered => ((1 2 0) (4 5 3))
And top it all off with an and:
(conj reordered 'and)
; => (and (1 2 0) (4 5 3))

Occasional null results with clojure's http.async.client and google

I have a result set in json with city names, and I'd like to get the lat-long for each. The following function works to an extent:
(:require [http.async.client :as client])
(defn get-geo-fact [row]
(let [
n (string/replace (:cityname row) " " "+")
url (str "http://maps.googleapis.com/maps/api/geocode/json?address="
n "&sensor=false")
resp (client/GET url) ]
(client/await resp)
(make-geo-fact row (json/read-json (client/string resp))) ))
That last call to make-geo-fact just returns an RDF rendering of the city coordinates. The problem I'm running into is that a run (of about 40 calls to this function) returns a few (3-5 lat-long pairs) null results for lat-longs. The cities that return null values differ from run to run - sometimes San Jose gets coordinates, and sometimes it doesn't.
I originally used slurp to grab the body of the url and got similarly-occasional nulls. I figured I wasn't waiting for the response properly, but switching to http.async.client doesn't seem to be doing the trick. Any ideas?
edit:
Here's the make-geo-fact function, which takes a "this team is located in this city" fact and a response from google maps, returning a vector of two triples that convey the latitude and longitude:
(defn make-geo-fact [row response]
(let [ g (:location (:geometry (first (:results response))))
lat (str "<" (:team row) ">"
" <http://www.nhl.com/latitude> \"" (:lat g)
"\"^^<http://www.w3.org/2001/XMLSchema#decimal> ." )
lon (str "<" (:team row) ">"
" <http://www.nhl.com/longitude> \"" (:lng g)
"\"^^<http://www.w3.org/2001/XMLSchema#decimal> ." ) ]
[lat lon] ))
And here's the function I call to kick the whole thing off:
(defn make-geo-facts []
(let [ a (bounce team-city (build "files/team-city.nt"))
f "files/geo-facts.nt" ]
(spit f (string/join "\n" (flatten (map get-geo-fact (:rows a)))))
f ))
Where the bounce function issues a SPARQL select query against an RDF model, which is instantiated with the build function.
edit 2
Here's a re-factor where make-geo-fact isn't needed:
(defn get-geo-fact [row]
(let [ n (string/replace (:cityname row) " " "+")
url (str "http://maps.googleapis.com/maps/api/geocode/json?address=" n "&sensor=false")
resp (client/GET url) ]
(-> (client/GET url)
client/await
client/string
json/read-json
:results
first
:geometry
:location )))
(defn make-geo-facts []
(let [ a (bounce tc-query (build "files/team-city.nt"))
f "files/geo-facts.nt"
*client* (client/create-client)]
(try (spit f (string/join "\n" (flatten (map get-geo-fact (:rows a))))))
(finally (client/close *client*)) ))
As you've said changing client implementations didn't make a difference.
I double checked and create a test for development version of http.async.client.
And always got responses with body.
Please provide make-geo-fact implementation.
Turns out my code needed a little sleep. I added (Thread/sleep 1000) to my core function and now I don't get null results:
(defn get-geo-fact [row]
(let [ n (string/replace (:cityname row) " " "+")
url (str "http://maps.googleapis.com/maps/api/geocode/json?address=" n "&sensor=false")
resp (client/GET url) ]
(Thread/sleep 1000)
(-> (client/GET url)
client/await
client/string
json/read-json
(make-geo-fact ,,, row ) )))