/lib/util.arc

http://github.com/alimoeeny/arc · Unknown · 479 lines · 379 code · 100 blank · 0 comment · 0 complexity · cdd2584a05eeab882d079fe9e84e5331 MD5 · raw file

  1. ; lib/util.arc: A collection of useful miscellanea
  2. ; Contributors:
  3. ; - Michael Arntzenius <daekharel@gmail.com>
  4. ; Persons we've shamelessly ripped off:
  5. ; - Conan Dalton <conan@conandalton.net>
  6. ; - absz (http://arclanguage.org/user?id=absz)
  7. ; - fallintothis (http://arclanguage.org/user?id=fallintothis)
  8. ; License: Do what you want, but it's not my fault!
  9. ; This license applies to all code in this file UNLESS OTHERWISE NOTED.
  10. ; Feel free to make additions to this file and push them to anarki. Keep it to
  11. ; stuff that is widely applicable. Don't remove or change the semantics of stuff
  12. ; other people added. Add yourself to the contributors list above if you want.
  13. ; If you wish to use a different license, make a comment around the code you've
  14. ; contributed noting the license. No licenses that don't permit modification
  15. ; (duh) and redistribution (double duh).
  16. ; Try and keep track of what is whose in sections with unknown licenses
  17. ; ("ripoffs").
  18. ; miscellaneous
  19. (def fst (a . _) " Returns its first argument. See also [[snd]] " a)
  20. (def snd (a b . _) " Returns its second argument. See also [[fst]] " b)
  21. (def bool (x)
  22. " Returns `t' if x is not nil, and `nil' otherwise. "
  23. (if x t))
  24. (def uniqs (lst)
  25. " Returns a list of gensyms, one for each element of `lst'. Elements
  26. of `lst' that are symbols are used as the base names for their
  27. corresponding gensyms.
  28. See also [[uniq]] "
  29. (map1 (iff asym uniq [uniq]) lst))
  30. (def gc () ($.collect-garbage))
  31. ; type checkers
  32. (def asym (x) " `t' iff `x' is a symbol. " (isa x 'sym))
  33. (def astring (x) " `t' iff `x' is a string. " (isa x 'string))
  34. (def atable (x) " `t' iff `x' is a table. " (isa x 'table))
  35. (def aint (x) " `t' iff `x' is an int. " (isa x 'int))
  36. (def anum (x) " `t' iff `x' is a num. Note that ints are not nums. "
  37. (isa x 'num))
  38. ; list manipulation
  39. ; 'reduce and 'rreduce have somewhat quirky behavior, well-suited to arithmetic
  40. ; functions, but otherwise hard to reason about. I usually prefer 'foldl and
  41. ; 'foldr.
  42. (def foldl (f v l)
  43. (if l (foldl f (f v car.l) cdr.l) v))
  44. (def foldr (f v l)
  45. (foldl flip.f v rev.l))
  46. (def foot (l)
  47. " Gets the last cons in a proper list. (Fails on dotted lists.) "
  48. (aif cdr.l foot.it l))
  49. (def join/d ls
  50. " Destructive join.
  51. See also [[join]]"
  52. (foldr (afn (a b) (if b (aif foot.a (do (scdr it b) a) b) a)) nil ls))
  53. (def classify (classifier seq)
  54. " Groups the elements of `seq' by `classifier', returning back a table,
  55. whose keys are the results of `classifier' and whose values are lists
  56. of the corresponding elements of `seq'. For example:
  57. arc> (classify type '(1 \"foo\" a 2 (b)))
  58. #hash((cons . ((b))) (int . (2 1)) (string . (\"foo\")) (sym . (a)))
  59. See also [[partition]] [[keep]] [[rem]] "
  60. (w/table h
  61. (each e seq
  62. (push e (h classifier.e)))))
  63. (def partition (test seq)
  64. " Equivalent to but more efficient than
  65. `(list (keep test seq) (rem test seq))'. See also [[keep]] [[rem]] "
  66. (let (passed failed) nil
  67. (each e seq
  68. (if test.e
  69. (push e passed)
  70. (push e failed)))
  71. (list rev.passed rev.failed)))
  72. (def unzip (xs)
  73. " Precisely as `zip', except that zip's `ls' is unzip's `xs'; so it takes one
  74. list of lists rather than any number of lists as arguments. Can be thought
  75. of as performing the inverse operation.
  76. See also [[zip]] "
  77. (apply map list xs))
  78. (def zip ls
  79. " Returns a list of lists; the n-th element of the result is a list of the
  80. n-th elements of the lists in `ls'. The length of the result is the length
  81. of the shortest list in `ls'; extra elements in other lists are discarded.
  82. See also [[unzip]] "
  83. (unzip ls))
  84. (def mklist (x)
  85. " Wraps atoms in a list; does nothing if `x' is already a list.
  86. See also [[atom]] [[alist]] [[list]] "
  87. (check x alist list.x))
  88. ; 'many was precisely the same function as 'acons, hence has been removed (any
  89. ; cons has a length > 0, since nil is not a cons). Also, 'popfind has been
  90. ; renamed 'pull1, to fit with the newly-added 'rem1.
  91. (def rem1 (test seq)
  92. " Returns a copy of `seq' with the first element that passes `test' removed.
  93. See also [[rem]] [[keep]] [[pull1]] [[pull]] "
  94. (zap testify test)
  95. (if alist.seq ((afn (s)
  96. (if no.s nil
  97. (f car.s) cdr.s
  98. (cons car.s (self cdr.s))))
  99. seq)
  100. (coerce (rem1 test (coerce seq 'cons)) 'string)))
  101. (mac pull1 (test place)
  102. " Removes the first element that passes `test' from `place'.
  103. See also [[pull]] [[rem1]] [[rem]] [[keep]] "
  104. `(= ,place (rem1 ,test ,place)))
  105. (= len= [is len._a _b])
  106. (= len- [- len._a _b])
  107. (= car< [< car._a car._b])
  108. (def keepkey (key lst) (keep [_ key] lst))
  109. (def mapkey (key lst) (map [_ key] lst))
  110. (def rand-pos (lst) (if lst (rand:len lst)))
  111. (mac pushend (elem lst)
  112. `(= ,lst (join ,lst (list ,elem))))
  113. (mac popnth (lst n)
  114. (w/uniq g1
  115. `(let ,g1 (,lst ,n)
  116. (= ,lst (+ (cut ,lst 0 ,n) (cut ,lst (+ 1 ,n))))
  117. ,g1)))
  118. (mac poprand (lst)
  119. (w/uniq g1
  120. `(if ,lst
  121. (let ,g1 (rand-pos ,lst)
  122. (popnth ,lst ,g1)))))
  123. ; combinators
  124. (def applied (f)
  125. " Returns a fn that calls `f' on the list of its arguments.
  126. For example, 'max is equivalent to `(applied [best > _])'. "
  127. (fn a (f a)))
  128. (def flip (f)
  129. " Flips the order of the first two arguments of `f'.
  130. For example: ((flip cons) 1 2) => (2 . 1) "
  131. (fn (x y . zs) (apply f y x zs)))
  132. (def curry (f . xs)
  133. " Partially applies (\"curries\") `f' to `xs'. "
  134. (fn ys (apply f (join xs ys))))
  135. (def const (x)
  136. " Creates a fn that takes any number of arguments and returns `x'. "
  137. (fn _ x))
  138. (def tfn _ " Ignores its arguments and returns t. " t)
  139. (def nilfn _ " Ignores its arguments and returns nil. " nil)
  140. (def norf fns
  141. " Creates a function which returns `t' iff none of `fns' return `t'
  142. on its arguments.
  143. See also [[orf]] [[andf]] [[nor]] "
  144. (complement (apply orf fns)))
  145. (def iff funs
  146. " Put simply: iff is to if as andf is to and. Specifically:
  147. (iff) => idfn
  148. (iff fun) => fun
  149. (iff test fun rest ...) => a fn that applies `fun' to its arguments if they
  150. pass `test', otherwise applies `(iff rest...)' to them.
  151. Examples:
  152. arc> ((iff alist car) '(x))
  153. x
  154. arc> ((iff alist car) 2)
  155. 2
  156. arc> ((iff < (fn (x y) x) (fn (x y) y)) 1 2)
  157. 1
  158. See also [[andf]] [[orf]] [[check]] [[idfn]] "
  159. (case len.funs
  160. 0 idfn
  161. 1 funs.0
  162. (withs ((test fun . rest) funs
  163. restfun (apply iff rest))
  164. (fn a (if (apply test a) (apply fun a)
  165. (apply restfun a))))))
  166. ; macros
  167. (mac mappendeach (var lst . body)
  168. " As 'mapeach, but using 'mappend instead of 'map.
  169. See also [[mapeach]] [[mappend]] [[each]] "
  170. `(mappend (fn (,var) ,@body) ,lst))
  171. (mac ado body
  172. " Anaphoric do.
  173. See also [[aif]] [[awhen]] [[aand]] "
  174. (aif cdr.body `(let it ,car.body (ado ,@it))
  175. car.body))
  176. ; now that pg has renamed 'assert to 'set, we're free to use it in its more
  177. ; conventional sense
  178. (mac assert (exp (o msg (+ "Assertion failed: "
  179. (tostring:ppr exp (len "Assertion failed: ") t))))
  180. " Errors with `msg' if `exp' evaluates to nil. "
  181. `(unless ,exp (err ,msg)))
  182. (mac asserts args
  183. " Asserts each expr in `args', with the default error message. "
  184. `(do ,@(map [list 'assert _] args)))
  185. (mac switchlet (var expr . cases)
  186. " Like 'caselet, except it (lazily) evals the expressions compared against.
  187. See also [[switch]] [[caselet]] [[case]]"
  188. `(let ,var ,expr
  189. ,((afn (args)
  190. (if (no cdr.args) car.args
  191. `(if (is ,var ,car.args) ,cadr.args
  192. ,(self cddr.args)))) cases)))
  193. (mac switch (expr . cases)
  194. " 'switch is to 'switchlet as 'case is to 'caselet.
  195. See also [[switchlet]] [[case]] [[caselet]]"
  196. `(switchlet ,(uniq) ,exp ,@cases))
  197. (mac dol (parms (test result) . body)
  198. " Like the standard lisp/scheme do loop, but with redundant inner parens
  199. removed."
  200. (w/uniq loop-name
  201. (let parms (tuples parms 3)
  202. `((rfn ,loop-name ,(map1 [_ 0] parms)
  203. (if ,test ,result
  204. (do ,@body (,loop-name ,@(map1 [_ 2] parms)))))
  205. ,@(map1 [_ 1] parms)))))
  206. ; binding forms
  207. (mac with/p (vars-vals . body)
  208. " Scheme/Common Lisp's `let' - ie: 'with with the parens added back.
  209. Easier to use in macro expansions than 'with.
  210. See also [[with]] [[withs/p]] "
  211. `(with ,(apply join vars-vals) ,@body))
  212. (mac withs/p (vars-vals . body)
  213. " Like Scheme/Common Lisp's `let*' - ie: 'withs with the parens added back.
  214. Easier to use in macro expansions than 'withs.
  215. See also [[withs]] [[with/p]] "
  216. `(withs ,(apply join vars-vals) ,@body))
  217. ; a 'with that works for defining recursive fns
  218. (mac withr/p (bindings . body)
  219. " Scheme's 'letrec.
  220. See also [[withr]] [[where]] "
  221. `(let ,(map1 car bindings) nil
  222. ,@(map [cons 'assign _] bindings)
  223. ,@body))
  224. (mac withr (bindings . body)
  225. " Scheme's 'letrec, with the redundant inner parens removed.
  226. See also [[withf]] [[letf]] [[where]] [[withr/p]] "
  227. `(withr/p ,pair.bindings ,@body))
  228. ; mutually recursive local fns
  229. (mac withf/p (fns . body)
  230. " Like 'withf, only with extra parens, as in 'with/p compared to 'with.
  231. See also [[withf]] [[with/p]] [[withr]] [[withr/p]] "
  232. `(withr/p ,(mapeach (name . rest) fns `(,name (fn ,@rest)))
  233. ,@body))
  234. (mac withf (fns . body)
  235. " Defines a set `fns' of mutually recursive local fns within `body'. Each
  236. three elements of `fn' correspond to a fn name, argument list, and body,
  237. so you'll need to use 'do if you want a multi-expression fn body.
  238. Example:
  239. arc> (withf (is-even (x) (case x 0 t (is-odd (- x 1)))
  240. is-odd (x) (case x 0 nil (is-even (- x 1))))
  241. (keep is-odd (range 0 5)))
  242. (1 3 5)
  243. See also [[letf]] [[withr]] "
  244. `(withf/p ,(tuples fns 3) ,@body))
  245. (mac letf (name args expr . body)
  246. " Defines a (possibly recursive) local fn `(fn ,args ,expr)' named `name'
  247. within `body'. Example:
  248. arc> (letf last (x) (aif cdr.x last.it car.x)
  249. (last '(x y z)))
  250. z
  251. See also [[withf]] [[withr]] "
  252. `(withf (,name ,args ,expr) ,@body))
  253. ; inspired by Haskell
  254. (mac where (expr . parms)
  255. " Binds `parms' and evaluates `expr'. Examples:
  256. arc> (where (square x)
  257. square [* _ _]
  258. x 2)
  259. 4
  260. Note that binding is recursive, but that actual assignment of values is done
  261. in the reverse of the order given, so any variables which are both bound and
  262. used in `parms' must be used in reverse dependency order:
  263. arc> (where x x (+ y y) y 0) ; this works as expected
  264. y
  265. arc> (where x y 0 x (+ y y)) ; this doesn't
  266. nil
  267. Essentially, this is a reversed form of Scheme's 'letrec,
  268. with many fewer parentheses. Inspired by Haskell's \"where\".
  269. See also [[withr]] [[withr/p]] [[withf]] "
  270. `(withr/p ,(rev:pair parms) ,expr))
  271. ; ripoffs - licenses unknown
  272. ; once-only by fallintothis
  273. ; http://arclanguage.org/item?id=9939
  274. ; CHANGED 2009-08-20:
  275. ; + take advantage of 'uniqs, 'with/p, 'zip
  276. ; + wrap 'names in a list if it's an atom
  277. ; + alter indentation slightly
  278. ; - Michael Arntzenius
  279. (mac once-only (names . body)
  280. (withs (names (check names alist list.names)
  281. gensyms (uniqs names))
  282. `(w/uniq ,gensyms
  283. `(with ,(list ,@(mappend list gensyms names))
  284. ,(with/p ,(zip names gensyms)
  285. ,@body)))))
  286. ; afnwith by Conan Dalton
  287. ; http://arclanguage.org/item?id=10055
  288. ; CHANGED 2009-08-15: added docstrings - Michael Arntzenius
  289. (mac rfnwith (name withses . body)
  290. " Convenient wrapper for applying an rfn using with-like syntax.
  291. `withses' is a list of argument names and their initial values.
  292. Best explained by example:
  293. arc> (rfnwith sum (x (range 1 3))
  294. (iflet (a . r) x (+ a (sum r)) 0))
  295. 6
  296. The above example macroexpands to:
  297. ((rfn sum (x) (iflet (a . r) x (+ a (sum r)) 0))
  298. (range 1 3))
  299. See also [[afnwith]] [[w/rfn]] [[rfn]] "
  300. (let w (pair withses)
  301. `((rfn ,name ,(map car w) ,@body) ,@(map cadr w))))
  302. (mac afnwith (withses . body)
  303. " Convenient wrapper for applying an afn using with-like syntax.
  304. `withses' is a list of argument names and their initial values.
  305. Best explained by example:
  306. arc> (afnwith (x (range 1 3))
  307. (iflet (a . r) x (+ a (self r)) 0))
  308. 6
  309. See also [[rfnwith]] [[w/afn]] [[afn]] "
  310. `(rfnwith self ,withses ,@body))
  311. ; ripoff: w/afn, by absz
  312. ; http://arclanguage.org/item?id=10125
  313. ; CHANGED 2009-08-15: added docstrings - Michael Arntzenius
  314. (mac w/rfn (name withses . body)
  315. " Convenient wrapper for applying an rfn using preexisting variables
  316. in `withses' as arguments. Best explained by example:
  317. arc> (let x (range 1 3)
  318. (w/rfn sum (x)
  319. (iflet (a . r) x (+ a (sum r)) 0)))
  320. 6
  321. The above example (w/rfn sum ...) macroexpands to:
  322. ((rfn sum (x) (iflet (a . r) x (+ a (sum r)) 0))
  323. x)
  324. See also [[w/afn]] [[rfnwith]] [[rfn]] "
  325. `(rfnwith ,name ,(mappend [list _ _] mklist.withses) ,@body))
  326. (mac w/afn (withses . body)
  327. " Convenient wrapper for applying an afn using the preexisting variables
  328. in `withses' as arguments. Best explained by example:
  329. arc> (let x (range 1 3)
  330. (w/afn (x)
  331. (iflet (a . r) x (+ a (self r)) 0)))
  332. 6
  333. The above example (w/afn (x) ...) macroexpands to:
  334. ((afn (x) (iflet (a . r) x (+ a (sum r)) 0))
  335. x)
  336. See also [[w/rfn]] [[afnwith]] [[afn]] "
  337. `(w/rfn self ,withses ,@body))
  338. ; start Andrew Wilcox (aw) code
  339. ; http://awwx.ws/span0.arc
  340. (def span (tst lst)
  341. ((afn (a lst)
  342. (if (and lst (tst (car lst)))
  343. (self (cons (car lst) a) (cdr lst))
  344. (list (rev a) lst)))
  345. nil lst))
  346. ; http://awwx.ws/xloop0.arc
  347. (mac xloop (withses . body)
  348. (let w (pair withses)
  349. `((rfn next ,(map car w) ,@body) ,@(map cadr w))))
  350. ; http://awwx.ws/implicit2.arc
  351. (mac implicit (name (o val))
  352. `(do (defvar ,name ($.make-parameter ,val))
  353. (mac ,(sym (string "w/" name)) (v . body)
  354. (w/uniq (param gv gf)
  355. `(with (,param (defvar-impl ,',name)
  356. ,gv ,v
  357. ,gf (fn () ,@body))
  358. ($ (parameterize ((,param ,gv)) (,gf))))))))
  359. ; http://awwx.ws/extend-readtable0.arc
  360. (def extend-readtable (c parser)
  361. ($
  362. (current-readtable
  363. (make-readtable (current-readtable)
  364. c
  365. 'non-terminating-macro
  366. (lambda (ch port src line col pos)
  367. (parser port))))))
  368. ; end aw code
  369. ; while with break and continue. by fallintothis
  370. ; http://arclanguage.org/item?id=12229
  371. (mac whilesc (test . body)
  372. `(point break (while ,test (point continue ,@body))))
  373. ; END RIPOFFS