I'm trying to cache lat/lon address pairs from Google Maps, so I need a data structure where the key is two ints (lat and lon). What's the simplest data structure for that?
I thought of two ways so far:
Nested hash:
{37.734608 {-121.913019 "San Ramon, CA" -121.6 "Tracy, CA"}}
Concat the two to make the key:
{"37.734608,-121.913019" "San Ramon, CA" "37.734608,-121.6" "Tracy, CA"}}
Am I missing any other solutions, and which would you recommend?
As you have lisp in your tags, the simplest and most idiomatic way is to use an association-list:
;; Sample in Scheme
> (define lat/lon (list (cons '(3.44 5.44)
'("blah" "3.44,5.44" "bloo"))
(cons '(37.734608 -121.913019)
'("San Ramon, CA" "37.734608,-121.6" "Tracy, CA"))))
> (assoc '(3.44 5.44) lat/lon)
=> ((3.44 5.44) "blah" "3.44,5.44" "bloo")
> (assoc '(37.734608 -121.913019) lat/lon)
=> ((37.734608 -121.913019) "San Ramon, CA" "37.734608,-121.6" "Tracy, CA")
check out Z-order but what are you using to store this? If its an RDBMS why can't you have a 2-field primary key?
The upside of Z-order is that if you sort by it then things that are close (physically) will generally be stored close (in memory/on disk) together
Related
How do you embed control characters (ie " or ' or :) in cl-dbi queries for sqlite3?
currently using a make-string-output/get-output-stream-string to build a variable that contains quoted data (json). Now, I want to be able to store the data in a sqlite3 db, but I'm obviously building the string wrong, because I get an error
DB Error: unrecognized token: ":" (Code: ERROR)
how do I escape characters in cl-dbi to pass them through to sqlite3?
EDIT - here's a brief passage of the JSON data that I'm trying to store (as text) in the sqlite3 db:
{
"type": "artificial",
Update: GAH! Took me a day to find an errant : in the prepared query string. :/
As far as I can make out, what you are trying to do is generate, in Lisp, some string which contains valid SQL, which itself contains an SQL literal string.
First of all this is an instance of an antipattern I call 'language in a string'. I keep thinking I have a big diatribe about this to point people at but it seems I haven't. Suffice it to say it's kind of the antithesis of what Lisp people have tried to achieve for more than 60 years and it's why we have SQL injection attacks and a lot of the other crap that afflicts us. But that battle is long lost and all we can do is try to avoid actually drowning in the sea of mud and rotting bits of people that now litters the battlefield.
So, to do this you need to be able to do two things.
You need to be able to generate an SQL literal string from a sequence of characters (or from a string). This means you need to know the syntax of literal SQL strings and in particular what characters are legal in them and how you express characters which are not.
You need to be able to interpolate this string into a CL string.
The second of these is trivial: this is what format's ~A directive does. Or if you want to get fancy you could use cl-interpol.
For the first, I don't know the syntax of SQL literal strings, but I will give an example which assumes the following simple rules:
literal strings are delimited by " characters;
the character \ escapes the following character to remove it of any special significance;
all other characters are allowed (this is almost certainly wrong).
Well, there are lots of ways of doing this, all of which involve walking along the sequence of characters looking for the ones that need to be escaped. Here is something reasonably horrible and quick which I wrote. It needs a macro called nlet which is Scheme's 'named let' construct, and it assumes TRO in the implementation (if your implementation does not do this, get one that does).
(defmacro nlet (name bindings &body forms)
"named let"
(multiple-value-bind (vars vals) (values (mapcar (lambda (b)
(etypecase b
(symbol b)
(cons (first b))))
bindings)
(mapcar (lambda (b)
(etypecase b
(symbol 'nil)
(cons
(unless (null (cddr b))
(error "bad binding ~A" b))
(second b))))
bindings))
`(labels ((,name ,vars ,#forms))
(,name ,#vals))))
(defun ->sql-string (seq)
;; turn SEQ (a sequence of characters) into a string representing an
;; SQL literal string (perhaps)
(nlet loupe ((tail (coerce seq 'list))
(accum '()))
(if (null tail)
(coerce (cons #\" (nreverse (cons #\" accum))) 'string)
(destructuring-bind (first . rest) tail
(loupe rest
(case first
((#\\ #\")
(append (list first #\\) accum))
(otherwise
(cons first accum))))))))
So now:
> (->sql-string "foo")
"\"foo\""
> (->sql-string '(#\f #\\ #\o #\" #\o))
"\"f\\\\o\\\"o\""
This is made ugly by the Lisp printer, but (see above) we can see what the strings actually are:
> (format t "~&select x from y where x.y = ~A;~%"
(->sql-string '(#\f #\\ #\o #\" #\o)))
select x from y where x.y = "f\\o\"o";
nil
And you can see that the SQL literal string obeys the rules I set out above.
Before using anything like this check what the rules are, because if you get them wrong you are possibly open to SQL injection attacks.
I am trying to learn common lisp through paradigms of artificial intelligence. one of the exercises in the book was to define a function that would return the last name of a given list without a certain suffix or word. As an example if the list contained bruce banner, phd it would return banner. here is my solution, however this would still return the last element regardless if it was a suffix or not
(Defun last-name (name)
(if (member (last name) *suffix*)
(Last-name (butlast name))
(last name)))
where
(defparameter *suffix* '( MD PHD DR))
a similar exercise was presented in the book omitting a prefix in a list using 'rest' instead of 'butlast' and returning the first element. That worked perfectly fine.
(defparameter *titles* '( Mr MRS MS SIR MADAM DR ADMIRAL MAJOR GENRAL ))
(Defun first-name (name)
(if (member (first name) *titles*)
(first-name (rest name))
(first name)))
I am not sure if I am missing anything in the last-name function or if my logic is incorrect. Any help is greatly appreciated!
Apologies if the text is long, it is my first post and I was not sure what level of detail I should include.
Thank you.
The reason is that last does not return the last element of a list, but a list containing the last element. That is:
(last '(bruce banner phd))
;; -> (PHD), not PHD
Actually, last gets an optional argument n after the list, argument which is set to 1 by default, and returns a list with the last n elements of the first parameter (see the manual).
So the function member does not return true.
Try to change the function in:
(defun last-name (name)
(if (member (car (last name)) *suffix*)
(Last-name (butlast name))
(car (last name))))
I've been using noir in a web project and I came up to the point of restricting access to users, depending on their access level (and sublevel) to all possible routes defined by a defpage macro. So originally I had
(defpage [:post "/mysite"] {:as input}
(if-not (has-reqd-user-level? :levelX :sublevelY "/grantedRoute")
(noir.response/redirect "/insufficientRights")
...))
And then I thought this would get rid of boilerplate code:
(defmacro defpage-with-user-level [level sublevel granted-route route data expr]
`(defpage ~route ~data
(if-not (has-reqd-user-level? ~level ~sublevel ~granted-route)
(noir.response/redirect "/insufficientRights")
~expr)))
Finally, we use it as follows:
(defpage-with-user-level :levelX :sublevelY "/grantedRoute"
[:post "/mysite"] {:as input}
(html
[:body [:h1 (str "Hello " (:name input) "!")]]))
But as mentioned in this post made by Rich Hickey, https://groups.google.com/forum/#!msg/clojure/4II-HKr_Pu0/2IcKit99cagJ , it feels a little bit awkward because of positional binding, which is not idiomatic when there already exist maps.
However, I've been looking for some examples or discussions regarding the use of destructuring bindings in macros, and sadly, I hadn't found any clear use of them, because of its unevaluated expressions being passed through all along.
So, the following solution came to my mind:
(defmacro defpage-with-user-level [dts expr]
`(defpage (:route ~dts) (:data ~dts)
(if-not (has-reqd-user-level? (:level ~dts) (:sublevel ~dts) (:granted-route ~dts))
(noir.response/redirect "/insufficientRights")
~expr)))
But now, it's not clear how to pass the data map that maps locals from :get and :post into a local as in the examples above.
Am I doing right leaving my first attempt untampered or do I really need to use the second approach? I hope not. Is there any other option? Please, let me know.
Your first solution is fine. What Rich was talking about was using plain old maps to passing data around rather then creating new types/classes for each type of data. Ex: you can represnt a user information using a simple map rather than creating a class to represent user data.
As far as your second attempt is concerned, you can use map de-structuring in macro as:
(defmacro defpage-with-user-level [{:keys [route data level sublevel granted-route]} expr]
`(defpage ~route ~data
(if-not (has-reqd-user-level? ~level ~sublevel ~granted-route)
(noir.response/redirect "/insufficientRights")
~expr)))
(defpage-with-user-level {:level :levelX
:sublevel :sublevelY
:granted-route "/grantedRoute"
:route [:post "/mysite"]
:data {:as input}}
(html
[:body [:h1 (str "Hello " (:name input) "!")]]))
I'm trying to make a function, which should give me a list of cities:
((London United-Kingdom)
(Paris France)
(New-York USA)
(Sydney Australia))
My code doesn't work, something is wrong but I don't know why. Here is my code:
(defun make-city (name country)
(list name country))
(defun cities
(list (make-city 'London 'United-Kingdom)
(make-city 'Paris 'France)
(make-city 'New-York 'USA)
(make-city 'Sydney 'Australia)))
Does anyone know where is a problem? It must be in (defun cities....), but where?
use defvar ("define variable") instead of defun ("define function") for cities.
Common Lisp is a "lisp-2", scheme is a "lisp-1".
I am learning Clojure by solving problems, I'm stuck with one of them, basically I have to find the top five strings in a log file.
Here is what I've got so far:
(ns topfive
(:import (java.io BufferedReader FileReader)))
(defn extract-query [line]
(.substring line (+ (.lastIndexOf line "=") 1) (.lastIndexOf line "]")))
(defn process-file [file-name, queries]
(with-open [rdr (BufferedReader. (FileReader. file-name))]
(doseq [line (line-seq rdr)]
(assoc queries (extract-query line) (inc (get queries (extract-query line) 0))))))
(process-file "in" {})
My problem is that queries does not contain anything, I've already checked that extract-queries returns the string I want, I thought that this might have something to do with the language itself, I've read that Clojure has immutability at language level, but this still does not seem a good point to me.
Could you suggest something about what I am doing wrong?
Clojure does have immutability at a low level, and hash-maps are immutable. So assoc doesn't mutate a map in-place, it creates a new map with an updated item in it, and returns the new map. You're calling assoc over and over, but discarding the results.
One fix is to use reduce instead of doseq. doseq iterates over a seq and does something to each item, but doesn't accumulate any results. So it should be used mostly for things that have side effects, e.g. printing to screen or file. reduce similarly iterates over a seq, but it does accumulate results.
(defn process-file [file-name, queries]
(with-open [rdr (BufferedReader. (FileReader. file-name))]
(reduce (fn [queries, line]
(assoc queries (extract-query line) (inc (get queries (extract-query line) 0))))
queries
(line-seq rdr))))
You could do a few things to simplify this a bit further. There's no need for a queries parameter to process-file, since it's always going to be an empty map to begin with. Your assoc line can be written more concisely using update-in and fnil; this also lets us avoid calling extract-query twice per line. You can replace all the calls to the Java Reader classes with the Clojure wrapper reader in clojure.java.io. You can replace your calls to substring with a regular expression; regex is more concise, but for large inputs your version might perform faster. You could also replace the anonymous function in my example with a sugary reader macro version using #(), though it's starting to look a bit noisy at this point, so I'd probably use let to make it read a bit better.
(ns topfive
(:require [clojure.java [io :as io]]))
(defn extract-query [line]
(nth (re-find #"query=([^]]+)" line) 1))
(defn process-file [file-name]
(with-open [rdr (io/reader file-name)]
(reduce #(let [search-term (extract-query %2)]
(update-in %1 [search-term] (fnil inc 0)))
{}
(line-seq rdr))))
in addition to Brians excellent answer: The threading macro may improve readability:
(ns stackoverflow
(:use [clojure.string :only [split]]
[clojure.java.io :only [reader]]))
(->> (reader "input.txt")
(line-seq)
(map #(last (split % #"=")))
(frequencies))