A syntactical comparison of
LFE, Erlang, Common Lisp, & Clojure
basics | ||||
---|---|---|---|---|
LFE | Erlang | common lisp | clojure | |
version used |
LFE 0.1.0 | Erlang 18.0 | SBCL 1.2 | Clojure 1.6 |
show version |
displayed by repl on startup (lutil:get-lfe-version) |
displayed by repl on startup erlang:system_info(otp_release). |
$ sbcl --version | displayed by repl on startup |
grammar and execution | ||||
LFE | Erlang | common lisp | clojure | |
compiler |
$ lfec module-name.lfe |
$ erlc module_name.erl |
||
standalone executable |
Use escript |
Use escript |
(sb-ext:save-lisp-and-die "executable" :executable t :toplevel 'function) |
|
interpreter |
$ lfe [flags] filename.lfe [args]
|
$ sbcl --script foo.lisp | specify full path to clojure
jar: java -cp clojure.jar clojure.main foo.clj |
|
shebang | #!/usr/bin/env lfe |
#!/usr/bin/env sbcl --script | specify full path to clojure
jar: #!/usr/bin/env java -jar clojure.jar |
|
repl |
$ lfe |
Erlang doesn't have a true REPL; it has a shell $ erl |
$ sbcl | $ java -jar /PATH/TO/clojure.jar |
command line program |
$ lfe -eval \ '(lfe_io:format "~p" (list (+ 1 2 3)))' |
$ erl -eval \ 'io:format("~p", [1 + 2 + 3])' \ -noshell -run init stop |
||
word separator |
whitespace | whitespace | whitespace | whitespace and commas |
end-of-line comment | (+ 1 1) ; adding | 1 + 1. % adding | (+ 1 1) ; adding | (+ 1 1) ; adding |
multiple line comment |
Only in source code; not in REPL (+ 1 #| adding stuff |# 1) |
(+ 1 #| adding stuff |# 1) |
||
variables and expressions | ||||
LFE | Erlang | common lisp | clojure | |
identifier | Cannot start with: |, \', ', ,, or # permitted characters: !, #, $, %, &, ', *, +, ,, -, ., /, 0-9, <, =, >, ?, @, A-Z, a-z, \, ^, _, |, ~, \% , ¡, ¢, £, ¤, ¥, ¦, §, ¨, ©, ª, «, ¬, \, ®, ¯, °, ±, ², ³, ´, µ, ¶, ·, ¸, ¹, º, », ¼, ½, ¾, ¿, À, Á, Â, Ã, Ä, Å, Æ, Ç, È, É, Ê, Ë, Ì, Í, Î, Ï, Ð, Ñ, Ò, Ó, Ô, Õ, Ö, ×, Ø, Ù, Ú, Û, Ü, Ý, Þ, ß, à, á, â, ã, ä, å, æ, ç, è, é, ê, ë, ì, í, î, ï, ð, ñ, ò, ó, ô, õ, ö, ÷, ø, ù, ú, û, ü, ý, þ, ÿ This has special meaning and is reserved: : |
case insensitive, cannot start
with digit excluded characters: SP ( ) " , ' ` : ; # | \ reserved for user macros: ? ! [ ] { } |
case sensitive, cannot start
with digit permitted characters: A-Z a-z 0-9 * + ! - _ ? these have special meaning or are reserved: / . : |
|
quoted identifier and escaped identifier |
(set |white space symbol| 3) |
(setq |white space symbol| 3) (setq white\ space\ symbol 3) |
none none |
|
local variable | ; parallel assignment: (let ((x 3) (y 4)) (+ x y)) ; sequential assignment: (let* ((x 3) (y (* x x))) (+ x y)) |
; parallel assignment: (let ((x 3) (y 4)) (+ x y)) ; sequential assignment: (let* ((x 3) (y (* x x))) (+ x y)) |
(let [x 3 y 4] (+ x y)) (let [[x y] [3 4]] (+ x y)) (let [x 3 y (* x x)] (+ x y)) |
|
global variable |
none |
none |
(defparameter *x* 3) ; doesn't change x if already set: (defvar *x* 3) |
(def x 3) |
remove
variable |
none |
Only in the shell: f(Var). |
(makunbound 'x) | (ns-unmap *ns* 'x) |
null |
none |
none |
nil '() | ; same value as null in Java: nil |
null test |
none |
none |
(null x) | (nil? x) |
identifier as value | 'x (quote x) |
'x (quote x) |
'x (quote x) |
|
identifier
test |
(is_atom x) |
is_atom(X). |
(symbolp 'x) | (symbol? 'x) |
identifier equality test | (== 'x 'x) (=:= 'x 'x) |
(eq 'x 'x) | (= 'x 'x) | |
non-referential identifier |
'foo | :foo | :foo | |
identifier
attributes set, get, remove |
none |
none |
(set 'x 13) (setf (get 'x :desc) "unlucky") (get 'x :desc) (remprop 'x :desc) |
; value must be instance of
clojure.lang.IObj: (def x (with-meta [13] {:desc "unlucky"})) (get (meta x) :desc) ; none |
arithmetic and logic | ||||
LFE | Erlang | common lisp | clojure | |
true and false | 'true 'false | true false | t nil | true false |
falsehoods |
'false | false | nil () | false nil |
logical operators |
(or (not 'true) (and 'true 'false)) | (not true) or (true and false). | (or (not t) (and t nil)) | (or (not true) (and true false)) |
relational operators |
== =:= /= =/= < > =< >= | == =:= /= =/= < > =< >= | = /= < > <= >= | = not= < > <= >= |
min and max | (lists:min 1 2 3) (lists:max 1 2 3) |
lists:min(1 2 3) lists:max(1 2 3) |
(min 1 2 3) (max 1 2 3) |
(min 1 2 3) (max 1 2 3) |
numeric predicates | is_number is_integer is_float | is_number is_integer is_float | numberp integerp rationalp floatp realp complexp |
number? integer? rational? float? none none |
arithmetic
operators |
+ - * / | + - * / | + - * / mod | + - * / mod |
integer
division and remainder |
(div 7 3) (rem 7 3) |
div(7 3). rem(7 3). |
(truncate 7 3) (rem 7 3) |
(quot 7 3) (rem 7 3) |
integer division by zero | exception error: badarith | exception error: an error occurred when evaluating an arithmetic expression | division-by-zero error | |
float division | float: (/ 7 (* 3 1.0)) (/ 7 (* 3 1)) |
float: 7 / 3 * 1.0. 7 / 3 * 1. |
rational: (/ 7 3) float: (/ 7 (* 3 1.0)) |
rational: (/ 7 3) float: (/ 7 (* 3 1.0)) |
float division by zero | exception error: badarith | exception error: an error occurred when evaluating an arithmetic expression | division-by-zero error | |
power | (math:pow 2 32) | math:pow(2, 32). | (expt 2 32) | returns float: (Math/pow 2 32) |
sqrt |
(math:sqrt 2) | math:sqrt(2). | (sqrt 2) | (Math/sqrt 2) |
sqrt -1 |
none | none | #c(0.0 1.0) | (Math/sqrt -1): NaN |
transcendental functions | math:exp math:log math:sin math:cos math:tan math:asin math:acos math:atan math:atan2 | math:exp math:log math:sin math:cos math:tan math:asin math:acos math:atan math:atan2 | exp log sin cos tan asin acos atan atan | Math/exp Math/log Math/sin Math/cos Math/tan Math/asin Math/acos Math/atan Math/atan2 |
float truncation | trunc | trunc | return two values, first is
integer: truncate round ceiling floor |
return integers: int Math/round return floats: Math/ceil Math/floor |
absolute
value and signum |
abs | abs | abs signum | Math/abs Math/signum |
integer overflow |
none | none | none; arbitrary-precision integers | clojure.lang.Numbers.throwIntOverflow exception |
float overflow |
none | none | floating-point-overflow error | not literals: -Infity NaN Infinity |
rational construction | none | none | (/ 3 7) ; literal: 3/7 |
(/ 3 7) ; literal: 3/7 |
rational decomposition | none | none | (numerator 3/7) (denominator 3/7) |
(numerator 3/7) (denominator 3/7) |
complex construction | none | none | #c(1 2) | none |
complex decomposition | none | none | (realpart #c(1 2)) (imagpart #c(1 2)) (phase #c(1 2)) (abs #c(1 2)) (conjugate #c(1 2)) |
none none |
random number uniform integer, uniform float, normal float |
(random:uniform 100) (random:uniform 1.0) |
random:uniform(100). random:uniform(1.0). |
(random 100) (random 1.0) none |
(def rnd (java.util.Random.)) (.nextInt rnd 100) (.nextFloat rnd) (.nextGaussian rnd) |
random seed | (random:seed) (random:seed 4 2 0) | random:seed(). random:seed(4 2 0). | (setq *random-state* (sb-ext:seed-random-state 17)) |
|
bit operators | bsl bsr band bor bxor bnot | bsl bsr band bor bxor bnot | ash left shift when 2nd argument positive logand logior logxor lognot | bit-shift-left bit-shift-right bit-and bit-or bit-xor bit-not |
binary, octal, and hex literals | #b101010 #o52 #x2a |
2#101010 8#52 16#2a |
#b101010 #o52 #x2a |
|
radix |
#7r60 | 7#60. | (format nil "~7r" 42) | |
strings | ||||
LFE | Erlang | common lisp | clojure | |
string
test |
(io_lib:printable_latin1_list "foo") | io_lib:printable_latin1_list("foo"). | (stringp "foo") | (string? "foo") |
string literal |
"foo bar" | "foo bar" | "foo bar" | "foo bar" |
unicode
test |
(io_lib:printable_unicode_list "foo") | io_lib:printable_unicode_list("foo"). | ||
unicode literal |
#"foo bar" (binary ("foo bar" utf8)) |
|||
newline in literal |
yes | yes | yes | yes |
literal escapes | \b \t \n \v \f \r \e \s \d | \" \\ | \b \t \n \f \r \" \\ \ooo \uhhhh | |
constructor |
(list #\f #\o #\o) | |||
format string | (lfe_io:format "~p: ~p ~.2f~n" '("Foo" 7 13.457)) | io:format("~p: ~p ~.2f~n", ["Foo", 7, 13.457]). | (format nil "~a: ~a ~,2f" "Foo" 7 13.457) | (String/format "%s: %d %.2f" (to-array ["Foo" 7 13.457])) |
format specifiers |
The following are for lfe_io:format ~~ literal ~ ~c ASCII character code ~f scientific notation ~g float for small numbers, scientific for large Each of the previous three may take additional precision formatting options ~s string ~w standard LFE syntax ~p pretty-printed LFE syntax ~W same as ~w, but takes maximum depth argument ~P same as ~p, but takes maximum depth argument ~B formats integer to given base ~X like ~B, but takes a prefix argument ~# like ~B, but uses #-separated Erlang prefix ~b like ~B, but prints lowercase ~x like ~X, but prints lowercase ~+ like ~#, but prints lowercase ~n newline ~i ignores next term |
The following are for io:format ~~ literal ~ ~c ASCII character code ~f scientific notation ~g float for small numbers, scientific for large Each of the previous three may take additional precision formatting options ~s string ~w standard Erlang syntax ~p pretty-printed Erlang syntax ~W same as ~w, but takes maximum depth argument ~P same as ~p, but takes maximum depth argument ~B formats integer to given base ~X like ~B, but takes a prefix argument ~# like ~B, but uses #-separated Erlang prefix ~b like ~B, but prints lowercase ~x like ~X, but prints lowercase ~+ like ~#, but prints lowercase ~n newline ~i ignores next term |
~a any type, human readable ~s any time, read parseable ~% newline ~~ tilde ~c character ~,5f 5 digits right of decimal mark ~d decimal ~x hex ~o octal ~b binary |
|
compare strings |
(string:equal "foo" "bar") Also the comparison operators work, e.g: (== "foo" "bar") (< "foo" "bar") (=< "foo" "bar") |
(string= "foo" "bar") (string< "foo" "bar") |
(.equals "foo" "bar") (.compareTo "foo" "bar") |
|
concatenate |
(string:concat "foo" "bar") (++ "foo" "bar") |
(concatenate 'string "foo " "bar " "bar") | (str "foo " "bar " "baz") | |
replicate |
(string:copies "foo" 3) (string:chars #\f 3) (lists:duplicate 3 #\f) |
(make-string 3 :initial-element #\f) | (String. (into-array (. Character TYPE) (repeat 3 \f))) |
|
translate case |
(string:to_lower "FOO") (string:to_upper "foo") |
(string-downcase "FOO") (string-upcase "foo") |
(.toLowerCase "FOO") | |
capitalize | none | none | ; "Foo Bar": (string-capitalize "foo bar") |
|
trim |
(string:strip " foo " 'both #\ ) |
(string-trim '(#\space #\tab #\newline) " foo ") |
(.trim " foo ") | |
pad on right, on left |
; right (string:right "foo" 10) (lfe_io:format "~10.8s~n" '("foo")) ; left (string:left "foo" 10) (lfe_io:format "~-10.8s~n" '("foo")) |
(format nil "~10a" "foo") (format nil "~10@a" "foo") |
||
number to string | (++ "Value: " (integer_to_list 8)) | (concatenate 'string "value: " (princ-to-string 8)) |
(str "Value: " 8) | |
string to number | (+ 7 (list_to_integer "12")) | (+ 7 (parse-integer "12")) (+ 73.9 (read-from-string ".037")) |
(+ 7 (Integer/parseInt "12")) (+ 73.9 (Float/parseFloat ".037")) |
|
split | (string:tokens "foo \tbar\nbaz" '(9 10 #\ )) | (cl-ppcre:split "[ \t\n]+" "foo bar baz") |
(seq (.split "foo bar baz" "[ \t\n]+")) |
|
string join | (string:join '("foo" "bar" "baz") " ") | (reduce (lambda (m o) (concatenate 'string m " " o)) '("foo" "bar" "baz")) |
(reduce #(str %1 " " %2) '("foo" "bar" "baz")) |
|
length |
(length "foo") | length("foo"). | (length "foo") | (.length "foo") |
index of substring | (string:str "foo bar" "bar") | string:str("foo bar", "bar"). | (search "bar" "foo bar") | (.indexOf "foo bar" "bar") |
extract substring |
; until position n (string:sub_string "foo bar" 5 7) ; for n chars (string:substr "foo bar" 5 3) |
(subseq "foo bar" 4 7) | (.substring "foo bar" 4 7) | |
character literal |
#\A #\Z #\a #\z #\? #\# Currently control characters are ASCII-code only |
#\a #\space #\newline #\backspace #\tab #\linefeed #\page #\return #\rubout | \a \newline \space \backspace \tab ? \formfeed \return ? | |
test characters |
(and (>= x 0) (=< x 255)) | (characterp #\x) (alpha-char-p #\x) (alphanumericp #\x) (digit-char-p #\7) (lower-case-p #\x) (upper-case-p #\X) |
(char? \x) | |
chr and ord |
'(97) #\a |
(code-char 97) (char-code #\a) |
(char 97) (int \a) |
|
to array of characters | '(#\f #\o #\o) | |||
character lookup |
(lists:nth 1 "foo") | (char "foo" 0) | (.charAt "foo" 0) | |
regular expressions | ||||
LFE | Erlang | common lisp | clojure | |
literal | use a string: "\\b\\d{5}\\b" |
use a string: "\\b\\d{5}\\b" |
use a string: "\\b\\d{5}\\b" |
#"\b\d{5}\b" |
character class abbrevations | . \d \D \s \S \w \W | . \d \D \s \S \w \W | . \d \D \s \S \w \W | . \d \D \s \S \w \W |
anchors | ^ $ \b \B | ^ $ \A \b \B \G \z \Z | ^ $ \b \B | ^ $ \A \b \B \G \z \Z |
match test | (re:run "foo bar" "bar") | (ql:quickload "cl-ppcre") (if (cl-ppcre:all-matches "1999" s) (format t "party!")) |
(re-find #"bar" "foo bar") | |
case insensitive match test | (re:run "foo BAR" "bar" '(caseless)) | (re-find #"(?i:lorem)" "Lorem") | ||
substitution |
(re:replace "foo bar baz" "ba" "EL") (re:replace "foo bar baz" "ba" #"EL" '(global)) |
(cl-ppcre:regex-replace "[^l]l" "hello" "EL") (cl-ppcre:regex-replace-all "[^l]l" "hello hello" "EL") |
(.replaceFirst "hello" "[^l]l" "XX") (.replaceAll "hello hello" "[^l]l" "XX") |
|
group capture | (re:run "2010-06-03" "(\\d{4})-(\\d{2})-(\\d{2})") | (let [[_ yr mn dy] (re-find #"(\d{4})-(\d{2})-(\d{2})" "2010-06-03")] yr) |
||
scan | (re-seq #"\w+" "dolor sit amet") | |||
backreference in match and substitution |
(re:run "sense and sensibility" "(sens|respons)e and \\1ibility") (re:replace "sense and sensibility" "(sens|respons)e and \\1ibility" "dumb and dumber") |
|||
dates and time | ||||
LFE | Erlang | common lisp | clojure | |
broken-down datetime type | No dedicated type; a 3-tuple is used for timestamps, and a 2-tuple for date and time: #(megaseconds seconds microseconds) #(#(year month day) #(hour minute second)) |
No dedicated type; a list of 9
values is used: second: 0-59 minute: 0-59 hour: 0-23 day of month: 1-31 month: 1-12 year: 4 digits day of week: 0-6 for Mon-Sun is daylight savings time: t or nil timezone: negated UTC offset in hours |
||
current datetime |
(erlang:timestamp) (calendar:local_time) |
(get-decoded-time) | (def dt (new java.util.Date)) | |
current unix epoch | (erlang:system_time 'seconds) | ; seconds since Jan 1, 1900 (get-universal-time) |
(/ (System/currentTimeMillis) 1000.0) | |
unix epoch to broken-down datetime | (decode-universal-time (get-unversal-time)) |
(def dt (new java.util.Date (System/currentTimeMillis))) |
||
broken-down datetime to unix epoch | (encode-universal-time 0 22 10 31 5 2015) | (/ (.getTime (new java.util.Date)) 1000.0) | ||
format datetime | (def s "yyyy-MM-dd HH:mm:ss") (def fmt (new java.text.SimpleDateFormat s)) (.format fmt (new java.util.Date)) |
|||
parse datetime | (def s "yyyy-MM-dd HH:mm:ss") (def fmt (new java.text.SimpleDateFormat s)) (.parse fmt "2015-05-30 09:14:14") |
|||
date parts | (multiple-value-bind (ss mi hr dy mo yr) (get-decoded-time) (list ss mi hr) ; quiesce warning (list dy mo yr)) |
(def cal (new java.util.GregorianCalendar)) (.setTime cal dt) (.get cal java.util.Calendar/DAY_OF_MONTH) (+ (.get cal java.util.Calendar/MONTH) 1) (.get cal java.util.Calendar/YEAR) |
||
time parts | (multiple-value-bind (ss mi hr) (get-decoded-time) (list ss mi hr)) |
(def cal (new java.util.GregorianCalendar)) (.setTime cal dt) (.get cal java.util.Calendar/HOUR_OF_DAY) (.get cal java.util.Calendar/MINUTE) (.get cal java.util.Calendar/SECOND) |
||
build broken-down datetime | (encode-universal-time 0 22 10 31 5 2015) | (let [yr 2015 mo 5 dy 31 hr 10 mi 22 ss 0] (def cal (new java.util.GregorianCalendar yr (- mo 1) dy hr mi ss))) |
||
lists | ||||
LFE | Erlang | common lisp | clojure | |
literal |
'(1 2 3) (quote (1 2 3)) |
'(1 2 3) (quote (1 2 3)) |
'(1 2 3) (quote (1 2 3)) |
|
constructor |
(list 1 2 3) | (list 1 2 3) | (list 1 2 3) | |
predicate |
(is_list x) | (listp x) | (list? x) | |
empty test | (== x '()) | nil and '() are synonyms and evaluate as false in a boolean context. All other values are true. | (empty? ()) | |
evaluating the empty list | () | nil | () | |
cons |
(cons 1 '(2 3)) | (cons 1 '(2 3)) | (cons 1 '(2 3)) | |
head |
(car '(1 2 3)) (set `(,h . ,_) '(1 2 3)) |
(car '(1 2 3)) (first '(1 2 3)) |
first | |
tail |
(cdr '(1 2 3)) (set `(,_ . ,t) '(1 2 3)) |
(cdr '(1 2 3)) (rest '(1 2 3)) |
(rest '(1 2 3)) (next '(1 2 3)) |
|
head and tail of empty list |
head exception error: badarg tail () cons pattern exception error: #(badmatch ()) |
both evaluate to nil | () | |
length |
(length '(1 2 3)) | (length '(1 2 3)) | (count '(1 2 3)) | |
equality test |
(= '(1 2 3) '(1 2 3)) | (equal '(1 2 3) '(1 2 3)) | (= '(1 2 3) '(1 2 3)) | |
nth element | indexed from one (lists:nth 3 '(1 2 3 4)) |
indexed from zero (nth 2 '(1 2 3 4)) |
(nth '(1 2 3 4) 2) | |
out-of-bounds behavior | exception error: function_clause | exception error: no function clause matching | nil | raises IndexOutOfBoundsException |
element index | none | none | (position 7 '(5 6 7 8)) | none |
concatenate |
(++ '(1 2 3) '(4 5 6)) (lists:append '(1 2 3) '(4 5 6)) |
(append '(1 2 3) '(4 5 6)) | (concat '(1 2 3) '(4 5 6)) | |
take |
(lists:sublist '(1 2 3 4) 2) | none | (take 2 '(1 2 3 4)) | |
drop |
(lists:nthtail 2 '(1 2 3 4)) | (nthcdr 2 '(1 2 3 4)) | (drop 2 '(1 2 3 4)) | |
last element |
(lists:last '(1 2 3)) | (car (last '(1 2 3))) | (last '(1 2 3)) | |
all but last element | (lists:droplast '(1 2 3)) | (butlast '(1 2 3)) | (butlast '(1 2 3)) | |
reverse |
(lists:reverse '(1 2 3)) | (reverse '(1 2 3)) | (reverse '(1 2 3)) | |
sort |
(lists:sort '(3 2 4 1)) (lists:sort #'>/2 '(3 2 4 1)) |
(sort '(3 2 4 1) '<) | (sort < '(3 2 4 1)) | |
dedupe |
(lists:usort '(1 1 2 3)) | (remove-duplicates '(1 1 2 3)) | ||
membership |
(lists:member 7 '(1 2 3)) | (member 7 '(1 2 3)) | ||
map | (lists:map (lambda (x)(* x x)) '(1 2 3)) | (mapcar (lambda (x) (* x x)) '(1 2 3)) | (map #(* % %) '(1 2 3)) | |
filter | (lists:filter (lambda (x) (> x 2)) '(1 2 3)) | (remove-if-not (lambda (x) (> x 2)) '(1 2 3)) ; remove-if returns complement |
(filter #(> % 2) '(1 2 3)) ; remove returns complement |
|
reduce | (lists:foldl #'*/2 1 '(1 2 3 4)) | (reduce '* '(1 2 3 4) :initial-value 1) | (reduce * 1 '(1 2 3 4)) | |
right fold | (lists:foldr #'-/2 0 '(1 2 3 4)) | (reduce '- '(1 2 3 4) :initial-value 0 :from-end t) |
none | |
iterate |
(lists:foreach (lambda (x) (io:format "~p~n" `(,x)) (io:format "~p~n" `(,(- x)))) '(1 2 3)) |
(dolist (x '(1 2 3)) (print x) (print (- x))) |
(doseq [x '(1 2 3)] (println x) (println (- x))) |
|
universal predicate |
(lists:all (lambda (i) (== 0 (rem i 2))) '(1 2 3 4)) |
(every (lambda (i) (= 0 (rem i 2))) '(1 2 3 4)) |
(every? #(= 0 (rem % 2)) '(1 2 3 4)) | |
existential predicate |
(lists:any (lambda (i) (== 0 (rem i 2))) '(1 2 3 4)) |
(some (lambda (i) (= 0 (rem i 2))) '(1 2 3 4)) |
(some #(= 0 (rem % 2)) '(1 2 3 4)) | |
list comprehension |
(lc ((<- file "ABCDEFGH") (<- rank (lists:seq 1 9))) (++ `(,file) (integer_to_list rank))) |
(for [file "ABCDEFGH" rank (range 1 9)] (format "%c%d" file rank)) |
||
shuffle |
none | none | none | (shuffle '(1 2 3 4)) |
set head | none | none | (defparameter *a* '(1 2 3)) (setf (car *a*) 3) |
none |
set tail | none | none | (defparameter *a* '(1 2 3)) (setf (cdr *a*) '(4 5 6)) |
none |
manipulate back | none | none | (defparameter *a* '(1 2 3)) (push 4 *a*) (pop *a*) |
|
flatten | (lists:flatten '(1 2 (3 (4)))) | (flatten '(1 2 (3 (4)))) | ||
associative array lookup |
(assoc 'key2 '((key1 "val1") (key2 "val2"))) | (assoc :key2 '((:key1 "val1") (:key2 "val2"))) | none, see note | |
flat associative array lookup |
none | none | (getf '(:key1 "val1" :key2 "val2") :key2) | none |
pair literal |
'(1 . 2) | '(1 . 2) | none | |
cons cell test | none | none | (cons '(1 . 2)) (not (atom '(1 . 2))) |
none |
translate elements recursively |
(sublis '((1 . 2) (3 . 4)) '(1 (3 3 (1)))) |
(sublis '((1 . 2) (3 . 4)) '(1 (3 3 (1)))) |
||
fixed-length arrays | ||||
LFE | Erlang | common lisp | clojure | |
literal |
#(1 2 3) | {1,2,3}. | #(1 2 3) | [1 2 3] |
constructor |
(tuple 1 2 3) | {1,2,3}. | (vector 1 2 3) | (vector 1 2 3) |
size |
(size #(1 2 3)) | size({1,2,3}). | (length #(1 2 3)) | (count [1 2 3]) |
lookup | (element 1 #(1 2 3)) | element(1, {1,2,3}). | (elt #(1 2 3) 0) or (aref #(1 2 3) 0) |
(nth [1 2 3] 0) |
update | (setelement 3 #(1 2 3) 4) | setelement(3, {1,2,3}, 4). | (setq v [1 2 3]) (setf (aref v 2) 4) |
(replace {2 4} [1 2 3]) |
out-of-bounds behavior | exception error: badarg | exception error: bad argument | raises sb-kernel:index-too-large-error | |
array to list |
(tuple_to_list #(1 2 3)) | tuple_to_list({1,2,3}). | (coerce #(1 2 3) 'list) | (seq [1 2 3]) |
list to array |
(list_to_tuple '(1 2 3)) | list_to_tuple [1,2,3]). | (coerce '(1 2 3) 'vector) | (vec '(1 2 3)) |
reverse | none | none | (reverse #(1 2 3)) | |
sort | none | none | (sort #(2 4 1 3) #'<) | |
map | (map 'vector (lambda (x) (* x x)) #(1 2 3)) | |||
filter | none | none | (remove-if-not (lambda (x) (> x 2)) #(1 2 3)) ; also remove-if |
|
reduce | none | none | ||
dictionaries | ||||
LFE | Erlang | common lisp | clojure | |
literal |
#M(a 1 b 2 c 3) | #{a => 1,b => 2,c => 3}. | none | ; clojure.lang.PersistentArrayMap: {"t" 1 "f" 0} |
constructor |
(map 'a 1 'b 2 'c 3) (maps:new) (maps:from_list '(#(a 1) #(b 2) #(c 3))) |
maps:new(). maps:from_list([{a,1},{b,2},{c,3}]). |
(defparameter *h* (make-hash-table :test 'equal)) ; default equality test is 'eql |
; immutable: (def ih (hash-map "t" 1 "f" 0)) |
predicate |
(is_map m) | is_map(M). | (hash-table-p *h*) | (map? ih) |
size |
(maps:size m) | maps:size(M). | (hash-table-count *h*) | (count ih) |
lookup |
(maps:get 'c #m(a 1 b 2 c 3)) (mref #m(a 1 b 2 c 3) 'c) (set `#m(c ,val) #m(a 1 b 2 c 3)) |
maps:get(c,#{a => 1,b => 2,c => 3}).
#{c := Val} = #{a => 1,b => 2,c => 3}. |
(gethash "t" *h*) | (get ih "t") (find ih "t") ; return -1 if not found: (get ih "m" -1) |
update |
(maps:update 'c 4 #m(a 1 b 2 c 3)) (mupd #m(a 1 b 2 c 3) 'c 4) (maps:put 'c 4 #m(a 1 b 2 c 3)) |
maps:update(c,4,#{a => 1,b => 2,c => 3}). maps:put(c,4,#{a => 1,b => 2,c => 3}). |
(setf (gethash "t" *h*) 1) | (def ih2 (assoc ih "t" 2)) |
missing key behavior | exception error: #(badkey ...) | exception error: {badkey,...} | returns nil | returns nil |
is key present |
(maps:is_key 'c m) | maps:is_key(c,M). | (nth-value 1 (gethash "t" *h*)) | (contains? ih "t") |
delete |
(maps:remove 'c m) (maps:remove keys m) |
maps:remove(c,M). maps:without([Keys], M) |
(remhash "t" *h*) | (def ih2 (dissoc ih "t")) |
merge | (maps:merge m1 m2) | maps:merge(M1,M2). | ; values in ih2 take precedence: (define ih3 (merge ih ih2)) |
|
invert | none | none | (require 'clojure.set) (define ih4 (clojure.set/map-invert ih)) |
|
iterate | (maps:map ...) creates new map | maps:map(...). creates new map | (maphash (lambda (k v) (print k) (print v)) *h*) |
(doseq [p ih] (println (first p)) (println (second p))) |
keys and values as lists |
(maps:keys m) (maps:values m) (maps:to_list |
maps:keys(M). maps:values(M). maps:to_list(M). |
none | (def hkeys (map (fn [p] (first p)) ih)) (def hvals (map (fn [p] (second p)) ih)) |
user-defined types | ||||
LFE | Erlang | common lisp | clojure | |
defstruct |
(defrecord account (id 0) (balance 0.0)) or (defrecord account id balance) |
(defstruct account id balance) | (defstruct account :id :balance) | |
struct |
(set a (make-account)) or (set a (make-account id 3 balance 17.12)) |
(setq a (make-account :id 3 :balance 17.12)) |
(def a (struct account 3 17.12)) | |
struct getter |
(account-id a) | (account-id a) | (:id a) | |
struct setter |
(set-account-balance a 100) | (setf (account-balance a) 0) | none | |
struct predicate |
none | none | (account-p a) | none |
functions | ||||
LFE | Erlang | common lisp | clojure | |
define function |
(defun add (x y) (+ x y)) | add(X,Y) -> X + Y. | (defun add (x y) (+ x y)) | (defn add [x y] (+ x y)) |
can function and variable share name | yes | no | yes | no |
optional argument | Convention: pass option tuple, list of tuples, or map | Convention: pass option tuple, list of tuples, or map | (defun add (a &optional b) (if (null b) a (+ a b))) |
(defn add ([a] a) ([a b] (+ a b))) no syntax error if called with more than 2 args: (defn add [a & [b]] (if (nil? b) a (+ a b))) |
variable number of arguments | no | no | (defun add (a &rest b) (if (null b) a (+ a (eval (cons '+ b))))) |
(defn add [a & b] (if (nil? b) a (+ a (apply + b)))) |
default value | none | none | (defun add (a &optional (b 0)) (+ a b)) |
(defn add ([a] (add a 0)) ([a b] (+ a b))) |
named parameter | Convention: pass option tuple, list of tuples, or map | Convention: pass option tuple, list of tuples, or map | (defun logarithm (&key number base) (/ (log number) (log base))) (logarithm :base 2 :number 8) |
(defn logarithm [{x :number b :base}] (/ (Math/log x)
(Math/log b))) (logarithm {:base 2 :number 8}) |
return multiple values | (defun sqrts (x) `#(,(math:sqrt x) ,(- (math:sqrt x)))) |
(defun sqrts (x) (values (sqrt x) (- (sqrt x)))) |
(defn sqrts [x] (list (Math/sqrt x) (- (Math/sqrt x)))) | |
assign multiple values to local variables | (let ((`#(,r1 ,r2) (sqrts 3))) r2) | (multiple-value-bind (r1 r2) (sqrts 3) r2) |
(let [[r1 r2] (sqrts 3)] r2) | |
assign multiple values to global variables | none | none | (multiple-value-setq (r1 r2) (sqrts 3)) |
none |
convert list to multiple values | multiple values are lists | multiple values are lists | (values-list '(1 2 3)) | multiple values are lists |
assign multiple values to list | multiple values are lists | multiple values are lists | (multiple-value-list (sqrts 3)) | multiple values are lists |
tail call optimization | yes | yes | yes for sbcl | yes with recur |
lambda | (lambda (x) (* x x)) | fun(X) -> X * X end. | (lambda (x) (* x x)) | #(* % %) (fn [x] (* x x)) ; shortcut notation with two args: #(* %1 %2) |
apply |
(funcall (lambda (x) (* x x)) 2) (apply (lambda (x) (* x x)) '(2)) |
apply(fun(X) -> X * X end, [2]). | ((lambda (x) (* x x)) 2) (apply #'(lambda (x) (* x x)) '(2)) |
(#(* % %) 2) ((fn [x] (* x x)) 2) (apply #(* % %) '(2)) |
execution control | ||||
LFE | Erlang | common lisp | clojure | |
progn |
progn prog1 prog2 | none | progn prog1 prog2 | do none none |
loop | none | none | (setq i 1) (loop (print "hello") (if (> i 10) (return) (setq i (+ i 1)))) |
(loop [i 1] (if (<= i 10) (do (println "hello") (recur (+ i 1))))) |
do | none | none | (do ((i 1) (sum 0)) ((> i 100) sum) (setq sum (+ sum i)) (setq i (+ i 1))) do* initializes serially |
none |
dotimes | none | none | (dotimes (i 10 nil) (format t "hello~%")) |
(dotimes [_ 10] (println "hello")) |
if |
(if (< x 0) (- x) x) |
if X < 0 -> -X; X >= 0 -> X end |
(if (< x 0) (- x) x) | (if (< x 0) (- x) x) |
when |
Only used in guards (defun print ((x y) (when (< x y)) "x is less than y") ((_ _) 'false)) |
Only used in guards |
(when (< x y) (print "x is less ") (print "than y")) |
(when (< x y) (println "x is less ") (println "than y")) |
cond | (cond ((> x 0) 1) ((=:= x 0) 0) ('true -1)) |
(cond ((> x 0) 1) ((= x 0) 0) (t -1)) |
(cond (> x 0) 1 (= x 0) 0 true -1) |
|
exceptions | ||||
LFE | Erlang | common lisp | clojure | |
error |
(error "failed") | (throw (Exception. "failed")) | ||
handle error | (handler-case (error "failed") (simple-error (e) (format t "error: ~a" e))) |
(try (throw (Exception. "failure")) (catch Exception e (printf "error: %s" (.getMessage e)))) |
||
define exception | (define-condition odd-err (error) ((num :accessor odd-err-num :initarg :num)) (:report (lambda (e s) (format s "odd number: ~a" (odd-err-num e))))) |
|||
throw exception | (error 'odd-err :num 7) | (throw (Exception. "failed")) | ||
catch exception | (handler-case (/ 1 0) (division-by-zero () (progn (format t "division by zero") nil))) |
(try (/ 1 0) (catch ArithmeticException _ (do (println "division by zero") nil))) | ||
restart-case | (defun halve (l) (mapcar (lambda (x) (restart-case (if (= (rem x 2) 0) (/ x 2) (error 'odd-error :num x)) (round-down () (/ (- x 1) 2)) (round-up () (/ (+ x 1) 2)))) l)) |
none | ||
invoke-restart | (handler-bind ((odd-err (lambda (c) (invoke-restart 'round-down)))) (halve '(1 2 4 9))) |
none | ||
finally clause | (unwind-protect (error "failure") (print "clean up")) |
(try (throw (Exception. "failure")) (finally (println "clean up"))) |
||
streams | ||||
LFE | Erlang | common lisp | clojure | |
standard file handles | *standard-input* *standard-output* *error-output* |
*in* *out* *err* |
||
end-of-file behavior | read-line returns two values,
the 2nd set to T at end-of-file. EOF-OF-FILE is signaled when reading past end of file. |
.readLine on a java.io.Reader object returns nil. | ||
read line from stdin | (setq line (read-line)) | (let [s (read-line)] (comment use s)) |
||
chomp |
read-line discards newline | |||
write line to stdout | (defun println (s) (format t "~a~%" s)) (println "hello") |
(println "hello") | ||
write formatted string to stdout | (format t "~s ~d: ~2$~%" "foo" 7 13.7) |
(printf "%s %d %.2f\n" "foo" 7 13.7) | ||
open file for reading | (setq in (open "/etc/hosts")) | ; f is java.io.Reader object: (let [f (clojure.java.io/reader "/etc/hosts")] (.readLine f)) |
||
open file for writing | (setq out (open "/tmp/test" :direction :output :if-exists :supersede)) | ; f is java.io.Writer object: (let [f (clojure.java.io/writer "/tmp/foo")] (.write f "lorem ipsum\n") (.close f)) |
||
open file for appending | (setq out (open "/tmp/test" :direction :output :if-exists :append)) | (let [f (clojure.java.io/writer "/tmp/foo" :append true)] (.write f "lorem ipsum\n") (.close f)) |
||
close file |
(close in) | (.close f) | ||
close file implicitly | (with-open-file (out #P"/tmp/test" :direction :output) (write-line "lorem ipsum" out)) | (with-open [f (clojure.java.io/reader "/etc/hosts")] (comment use f)) |
||
read line |
(setq line (read-line f)) | (.readLine f) | ||
iterate over file by line | (loop [line (.readLine f)] (if (not= line nil) (do (println line) (recur (.readLine f))))) |
|||
read file into array of strings | (vec (line-seq f)) | |||
read file into string | (let [s (slurp "/etc/hosts")] (print s)) |
|||
write string |
(.write f s) | |||
write line | (.write f (println-str s)) | |||
flush file handle |
(f .flush) | |||
file handle
position get, set |
; arg is characters from current
position; ; moving backward not possible: (.skip f 1000) ; arg is max characters to buffer: (.mark f 1000000) ; move to position saved when .mark was called: (.rest f) |
|||
in memory stream | (setq f (make-string-input-stream "lorem ipsum")) (read-line f) (setq f2 (make-string-output-stream) (write-string "lorem ipsum) (get-output-stream-string out) |
; use *in* to read from string: (with-in-str "lorem ispum" (read-line)) ; use *out* to write to string: (with-out-str (println "lorem ipsum")) |
||
emacs buffers | ||||
list buffers | ||||
current buffer get and set |
||||
clear buffer | ||||
point get and set |
||||
search and set point | ||||
insert at string point | ||||
current buffer as string | ||||
insert file contents at point | ||||
mark get and set |
||||
files | ||||
LFE | Erlang | common lisp | clojure | |
file test, regular file test | (osicat:file-exists-p "/tmp/foo") (osicat:regular-file-exists-p "/tmp/foo") |
(.exists (io/file "/etc/hosts")) | ||
file size | (.length (io/file "/etc/hosts")) | |||
is file readable, writable, executable | (.canRead (io/file "/etc/hosts")) (.canWrite (io/file "/etc/hosts")) (.canExecute (io/file "/etc/hosts")) |
|||
set file permissions | ||||
last modification time | ; Unix epoch in milliseconds: (.lastModified (java.io.File. "/tmp/foo")) |
|||
copy file, remove file, rename file | (cl-fad:copy-file #P"/tmp/foo" #P"/tmp/bar") (delete-file #P"/tmp/foo") (rename-file #P"/tmp/bar" #P"/tmp/foo") |
(clojure.java.io/copy (java.io.File. "/tmp/foo") (java.io.File. "/tmp/bar")) (clojure.java.io/delete-file "/tmp/foo") (.renameTo (java.io.File. "/tmp/bar") (java.io.File. "/tmp/foo")) |
||
create symlink, symlink test, get target | (osicat:make-link "/tmp/hosts" :target "/etc/hosts") | |||
temporary file | ; java.io.File: (java.io.File/createTempFile "foo" ".txt") |
|||
directories | ||||
LFE | Erlang | common lisp | clojure | |
build pathname | (make-pathname :directory '(:absolute "etc") :name "hosts") |
(require '[clojure.java.io :as io]) ; returns java.io.File; ; convert to string with .getPath: (io/file "/etc" "hosts") |
||
dirname and basename | (pathname-directory #P"/etc/hosts") (pathname-name #P"/etc/hosts") |
(require '[clojure.java.io :as io]) (.getParent (io/file "/etc/hosts")) (.getName (io/file "/etc/hosts")) |
||
absolute pathname | (.getCanonicalPath (java.io.File. "..")) | |||
iterate over directory by file | (dolist (file (osicat:list-directory "/tmp")) (format t "~a~%" file)) | ; file-seq returns java.io.File
objects for files ; in arg directory and any subdirs recursively. (filter #(= (.getParent %) "/etc") (file-seq (clojure.java.io/file "/etc"))) |
||
make directory | (require '[clojure.java.io :as io]) (.mkdir (io/file "/tmp/foo")) |
|||
recursive copy | ||||
remove empty directory | (delete-directory "/tmp/foo.d") | (clojure.java.io/delete-file "/tmp/foo.d") | ||
remove directory and contents | (osicat:delete-directory-and-files "/tmp/foo.d") | |||
directory test | (osicat:directory-exists-p #P"/etc") | (.isDirectory (io/file "/etc")) | ||
processes and environment | ||||
LFE | Erlang | common lisp | clojure | |
command line arguments | *posix-argv* | *command-line-args* | ||
program name | ||||
environment variables | (posix-getenv "HOME") | (System/getenv "HOME") | ||
user id and name | ||||
exit | ||||
external command | (run-program "ls" '( "/etc")) | (.exec (Runtime/getRuntime) "ls") | ||
command substitution | ||||
libraries and namespaces | ||||
LFE | Erlang | common lisp | clojure | |
complete example | $ cat b/a.clj (ns b.a) (def x 3) $ java -cp clojure.jar:. clojure.main => (require 'b.a) => b.a/x 3 |
|||
compile library | (compile-file "a.lisp") | (compile 'a) | ||
load library |
(load "a.lisp") | (require 'a) | ||
load library in subdirectory | (load "b/a.lisp") | (require 'b.a) | ||
hot patch | (load "a.lisp") | (require 'b.a :reload) | ||
load error | raises sb-int:simple-file-error | raises FileNotFoundException | ||
library path | contains working directory at startup | same as path used by java VM | ||
library path environment variable | none | CLASSPATH | ||
library path command line option | none | $ java -cp /foo/bar:/baz/quux | ||
namespace declaration | (defpackage :foo) | (ns mconst) | ||
subnamespace declaration | none | ; must be in b/a.clj: (ns b.a) |
||
namespace separator | : | . and / | ||
import definitions | ; set current *package* to foo and
import symbol twiddle from bar: (defpackage :foo (:import-from :bar :twiddle)) |
|||
import all definitions in namespace | ; set current *package* to foo and
import symbols from bar: (defpackage :foo (:use :bar)) |
|||
namespace shadow avoidance | ||||
identifier shadow avoidance | ||||
package manager help | ||||
list installed packages | ||||
search packages | (ql:system-apropos "time") | |||
install package | ; install quicklisp (load "~/quicklisp/setup.lisp") (ql:quickload "osicat") |
|||
remove package | ||||
objects | ||||
LFE | Erlang | common lisp | clojure | |
define class | (defclass rectangle () ( (height :accessor rectangle-height :initarg :height) (width :accessor rectangle-width :initarg :width))) |
use java: public class Rectangle { public float height; public float width; public Rectangle(float h, float w) { this.height = h; this.width = w; } public void setHeight(float h) { this.height = h; } public void setWidth(float w) { this.width = w; } |
||
make instance | (make-instance 'rectangle :height 3 :width 7) |
(import 'Rectangle) (def r (Rectangle. 7 3)) |
||
read attribute |
(rectangle-height rect) | (.height r) | ||
write attribute |
(setf (rectangle-height rect) 4) | (.setHeight r 8) | ||
define method | (defmethod area ((figure rectangle)) (* (rectangle-height figure) (rectangle-width figure))) |
(defmulti area class) (defmethod area Rectangle [r] (* (.height r) (.width r))) |
||
invoke method |
(area rect) | (area r) | ||
universal superclass | standard-object t | Object | ||
multiple inheritance | yes | only one direct superclass; can implement multiple interfaces | ||
lisp macros | ||||
LFE | Erlang | common lisp | clojure | |
backquote and comma | (setq op '+) (eval `(,op 1 1)) |
(def op +) (eval `(,op 1 1)) |
||
defmacro | (defmacro rpn (arg1 arg2 op) (list op arg1 arg2)) |
(defmacro rpn [arg1 arg2 op] (list op arg1 arg2)) |
||
defmacro w/ backquote | (defmacro rpn (arg1 arg2 op) `(,op ,arg1 ,arg2)) |
(defmacro rpn [arg1 arg2 op] `(~op ~arg1 ~arg2)) | ||
macro predicate | (macro-function rpn) | none | ||
macroexpand | (macroexpand ’(rpn 1 2 +)) | (macroexpand '(rpn 1 2 +)) | ||
splice quote | (defmacro add ( &rest args ) `(+ ,@args)) |
(defmacro add [ & args ] `(+ ~@args)) | ||
recursive macro | (defmacro add (a &rest b) `(if (null ',b) (+ ,a) (+ ,a (add ,@b)))) |
(defmacro add ([a] `(+ ~a)) ([a & b] `(+ ~a (add ~@b)))) | ||
hygienic |
no | with # suffix | ||
local values | (defmacro square-sum (x y) (let ((sum (gensym))) `(let ((,sum (+ ,x ,y))) (* ,sum ,sum)))) |
(defmacro two-list [x] `(let [arg# ~x] (list arg# arg#))) | ||
reflection | ||||
LFE | Erlang | common lisp | clojure | |
inspect type |
(type-of '(1 2 3)) (typep '(1 2 3) 'list) (listp '(1 2 3)) |
(= (type 1) java.lang.Long) (= (class 1) java.lang.Long) (integer? 1) |
||
instance-of | instance? | |||
basic types | logical and numeric: bignum bit complex double-float fixnum float integer long-float nil null number ratio rational real short-float signed-btye single-float t unsigned-byte symbols and strings: base-character character extended-character keyword simple-string standard-char string symbol data structures: array atom bit-vector cons hash-table list sequence simple-array simple-bit-vector simple-vector vector other: compiled-function function package pathname random-state stream |
|||
sequence data types | list vector | all collections and strings | ||
get docstring |
(describe #'mapcar) | (doc map) | ||
define function with docstring | (defun add (x y) "add x and y" (+ x y)) |
(defn add "add x and y" [x y] (+ x y)) |
||
apropos and documentation search | none | (apropos #"^add$") (find-doc #"add \S+ and \S+") |
||
java interoperation | ||||
LFE | Erlang | common lisp | clojure | |
new | (def rnd (new java.util.Random)) (def rnd (java.util.Random.)) |
|||
method | (. rnd nextFloat) (.nextFloat rnd) (. rnd nextInt 10) (.nextInt rnd 10) |
|||
class method |
(Math/sqrt 2) | |||
chain |
||||
import | (import '(java.util Random)) (def rnd (Random.)) |
|||
to java array | (to-array '(1 2 3)) (into-array Integer '(1 2 3)) |
|||
__________________________________________ | __________________________________________ | __________________________________________ | __________________________________________ |