-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A first PR for a standard lib #30
base: master
Are you sure you want to change the base?
Changes from 37 commits
919235d
6896de5
9ee3f53
1c90205
d778b11
3e82b23
bf7ee54
7aad918
08262cc
ffbfb3d
edd9b56
4fffea8
0db9e06
7e2d391
2ca2dc9
a9ad888
8b6daf0
bda6942
9241d45
a262a78
771cf88
926b318
1478aa2
59925b5
7e98dfd
3249f61
6da7793
122bbc1
1374831
c4b2312
cb58bef
e7ad840
e9ff2c3
e996b11
4895eb9
2fab48c
37936e3
208f72f
88b1653
abe766d
5aafb28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
eval-main : | ||
lurkrs bool-test.lurk |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
!(:load "bool.lurk") | ||
|
||
;; test that (bool-neg nil) evaluates to t | ||
!(:assert (bool-neg nil)) | ||
!(:assert (eq nil (bool-neg t))) | ||
|
||
;; four test cases for eq-bool | ||
!(:assert (eq nil (eq-bool t nil))) | ||
!(:assert (eq nil (eq-bool nil t))) | ||
|
||
;; test that (bool-neg t) evaluates to nil | ||
!(:assert (eq nil (bool-neg t))) | ||
|
||
;; test logical and | ||
!(:assert (and t t)) | ||
!(:assert (eq nil (and t nil))) | ||
!(:assert (eq nil (and nil t))) | ||
!(:assert (eq nil (and nil nil))) | ||
|
||
;; test logical or | ||
!(:assert (or t t)) | ||
!(:assert (or t nil)) | ||
!(:assert (or nil t)) | ||
!(:assert (eq nil (or nil nil))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
;; some boolean operations | ||
|
||
;; negate a boolean | ||
(let ((bool-neg (lambda (x) (if x nil t)))) (current-env)) | ||
|
||
;; not equal | ||
(let ((neq (lambda (x y) (bool-neg (eq x y))))) (current-env)) | ||
|
||
;; logical and | ||
(let ((and (lambda (b1 b2) (if b1 (if b2 t))))) (current-env)) | ||
|
||
;; logical or | ||
(let ((or (lambda (b1 b2) (if b1 t (if b2 t))))) (current-env)) | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
eval-main : | ||
lurkrs list-test.lurk |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
!(:load "../safe-arith/safe-arith.lurk") | ||
!(:load "../bool/bool.lurk") | ||
!(:load "list.lurk") | ||
|
||
;; test snoc | ||
!(:assert-eq '(1 2 3 4) (snoc 4 '(1 2 3))) | ||
|
||
;; test reverse | ||
!(:assert-eq '(3 2 1) (reverse '(1 2 3))) | ||
!(:assert-eq '() (reverse '())) | ||
|
||
;; test cadr | ||
!(:assert-eq 2 (cadr '(1 2 3))) | ||
|
||
;; test cddr | ||
!(:assert (= 3 (cddr '(1 2 3)))) | ||
|
||
;; test list-n | ||
!(:assert-eq '(nil nil) (list-n 2)) | ||
!(:assert-eq 10 (length (list-n 10))) | ||
|
||
;; test numbered-list-n | ||
!(:assert-eq '(0 1 2 3) (numbered-list-n 3)) | ||
!(:assert-eq 11 (length (numbered-list-n 10))) | ||
|
||
;; test nth | ||
!(:assert-eq 4 (nth 2 '(1 2 4 3))) | ||
|
||
;; test last | ||
!(:assert-eq 3 (last '(1 2 3))) | ||
|
||
;; test list-update | ||
!(:assert-eq '(1 2 3) (list-update 1 2 '(1 1 3))) | ||
|
||
;; test length | ||
!(:assert-eq 3 (length '(1 2 3))) | ||
!(:assert-eq 0 (length '())) | ||
|
||
;; test apply1 | ||
!(:assert-eq 7 (apply1 (lambda (x y) (+ x y)) '(3 4))) | ||
!(:assert-eq 7 (apply1 (lambda () 7) '())) | ||
|
||
;; test map | ||
!(:assert-eq '(2 4 6) (map (lambda (x) (* 2 x)) '(1 2 3))) | ||
!(:assert-eq '() (map (lambda (x) (* 2 x)) '())) | ||
|
||
;; test fold | ||
!(:assert-eq 6 (fold (lambda (x y) (+ x y)) 0 '(1 2 3))) | ||
!(:assert-eq 0 (fold (lambda (x y) (+ x y)) 0 '())) | ||
|
||
;; test concat | ||
!(:assert-eq '(1 2 3 4) (concat '(1 2) '(3 4))) | ||
!(:assert-eq '(1 2 3 4) (concat '() '(1 2 3 4))) | ||
!(:assert-eq '(1 2 3 4) (concat '(1 2 3 4) '())) | ||
|
||
;; test take | ||
!(:assert-eq '(1 2 3) (take 3 '(1 2 3 4 5))) | ||
!(:assert-eq '() (take 0 '(1 2 3 4 5))) | ||
!(:assert-eq '(1 2 3) (take 5 '(1 2 3))) | ||
|
||
;; test drop | ||
!(:assert-eq '(3 4 5) (drop 2 '(1 2 3 4 5))) | ||
!(:assert-eq '() (drop 6 '(1 2 3 4 5))) | ||
|
||
;; test filter | ||
!(:assert-eq '(3 4 5) (filter (lambda (x) (> x 2)) '(1 2 3 4 0 5))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
;; !(:load "../bool/bool.lurk") | ||
|
||
;; a list is of the form (cons a (cons b (cons ... (cons nil)...))) | ||
|
||
;; snoc : appends to the right-hand side of a list (opposite of cons) | ||
(letrec | ||
((snoc-iter (lambda (old-list new-list) | ||
(if (car old-list) | ||
(snoc-iter (cdr old-list) (cons (car old-list) new-list)) | ||
new-list))) | ||
(snoc (lambda (x lst) (snoc-iter (snoc-iter lst '()) (cons x nil))))) | ||
(current-env)) | ||
|
||
;; reverse : reverses the order of a list | ||
(letrec | ||
((reverse-iter (lambda (list1 list2) | ||
(if (car list1) | ||
(reverse-iter (cdr list1) (cons (car list1) list2)) | ||
list2))) | ||
(reverse (lambda (list) (reverse-iter list nil)))) | ||
(current-env)) | ||
|
||
;; cxxxr combinations (up to 3) | ||
(let ((cadr (lambda (list) (car (cdr list)))) | ||
(caar (lambda (list) (car (car list)))) | ||
(cdar (lambda (list) (cdr (car list)))) | ||
(cddr (lambda (list) (cdr (cdr list)))) | ||
(cdddr (lambda (list) (cdr (cdr (cdr list))))) | ||
(cddar (lambda (list) (cdr (cdr (car list))))) | ||
(cdaar (lambda (list) (cdr (car (car list))))) | ||
(cdadr (lambda (list) (cdr (car (cdr list))))) | ||
(caaar (lambda (list) (car (car (car list))))) | ||
(cadar (lambda (list) (cdr (cdr (car list))))) | ||
(caadr (lambda (list) (car (car (cdr list))))) | ||
(caddr (lambda (list) (car (cdr (cdr list)))))) | ||
(current-env)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LIST probably requires a macro or language support to be done right (variable number of arguments). Explicit use of EVAL is probably not desirable, but if it were, you probably want the current env to be involved. I'm chalking this up to probably not being a useful function as written. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I came up with that one, and i had a newer version that uses current-env, so that you can do nested lists. (list (lambda (q)
(if (car q)
(cons (eval (car q) (current-env))
(list (cdr q)))
nil))) I don't see any other way to produce a literal list (that's not quoted, but rather requires eval'ing args). I mean, aside from a bunch of consing. Obviously using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a clue that the concept involved is off. If we really want a function that takes a list and outputs a list, we can just use the identity function. Normal Lisp LIST, apart from being variadic, doesn't perform explicit evaluation. It just so happens that the args are evaluated before LIST ever sees them. For example, here is how SBCL defines LIST:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understand this. What I really want is lisp's LIST, but I don't think that's possible to define in lurk itself right now. I'm sure you understand why I want it, it's for the same purpose everyone uses LIST in lisp - to make a literal list with expressions in it that I want evaluated first. Without that the only way I can think to make such a list is with repeated consing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I understand. I think one urge is for Lisp's LIST, which can indeed be implemented with macros or when we have support for &REST parameters. I think you also want something that is definitionally not LIST, but that would accomplish a practical purpose. That something is basically MAP-EVAL of some flavor — which is fine. I just would not call it LIST, since that's confusing and also will clash with some future implementation of LIST which works as it should. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok yes I see your point, I started out trying to implement LIST but realized it's not possible and then kept the name on something that's not the same. What I ended up with is basically (map eval ...) like you said. I am fine with whatever it is not being in the stdlib but I will need something like this as a utility function at least until there's something better. I am ok with just copying it around. Putting it in the stdlib is probably bad because later it will have no purpose and should go away. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not even saying not to have such a function. I'm just saying don't call it LIST. You could call it MAP-EVAL or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah, if we have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be a little more complicated than you want to try to inline by hand. Remember:
As a test, you want to make sure something like this works:
I think for that to work, you probably need to also pass |
||
|
||
;; list-n : make an empty list of size n | ||
(letrec | ||
((list-n-iter (lambda (n list) | ||
(if (= n 0) | ||
list | ||
(list-n-iter (- n 1) (cons nil list))))) | ||
(list-n (lambda (n) (list-n-iter n nil)))) | ||
(current-env)) | ||
|
||
;; numbered-list-n : make a numbered list from 0 to n of length (n + 1) | ||
(letrec | ||
((numbered-list-iter (lambda (n list) | ||
(if (= n 0) | ||
(cons 0 list) | ||
(numbered-list-iter (- n 1) (cons n list))))) | ||
(numbered-list-n (lambda (n) | ||
(numbered-list-iter n nil)))) | ||
(current-env)) | ||
|
||
|
||
;; nth : get the nth (0-indexed) value of a list | ||
(letrec ((nth (lambda (place list) | ||
(if (car list) | ||
(if (= 0 place) | ||
(car list) | ||
(nth (- place 1) (cdr list))))))) | ||
(current-env)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can just be called Also I think we have automatic currying here, so you can try (letrec ((length_iter (lambda (cntr lst)
(if (car lst)
(length_iter (+ 1 cntr) (cdr lst))
cntr)))
(list_length (length_iter 0)))
(current-env)) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you say the same goes for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
;; last : get the last element of a list | ||
(let ((last (lambda (list) (car (reverse list))))) (current-env)) | ||
|
||
;; list-update : update the nth (0-indexed) value of a list | ||
;; if (< (length list) n) then list-update returns | ||
;; an unchanged list | ||
(letrec | ||
((list-update-iter (lambda (place value old-list new-list) | ||
(if (car old-list) | ||
(if (= 0 place) | ||
(list-update-iter | ||
(- place 1) | ||
nil | ||
(cdr old-list) | ||
(snoc value new-list)) | ||
(list-update-iter | ||
(- place 1) | ||
value | ||
(cdr old-list) | ||
(snoc (car old-list) new-list))) | ||
new-list))) | ||
(list-update (lambda (place value list) | ||
(list-update-iter place value list nil)))) | ||
(current-env)) | ||
dhsorens marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
;; length : determines the (0-indexed) length of a list | ||
(letrec | ||
((length-iter (lambda (lst cntr) | ||
(if lst | ||
(length-iter (cdr lst) (+ 1 cntr)) | ||
cntr))) | ||
(length (lambda (lst) (length-iter lst 0)))) | ||
(current-env)) | ||
|
||
;; apply : a preliminary version of apply | ||
(letrec | ||
((apply1 (lambda (f list) | ||
(if list | ||
(if (cdr list) | ||
(apply1 (f (car list)) (cdr list)) | ||
(f (car list))) | ||
(f))))) | ||
(current-env)) | ||
|
||
;; map : a list map function | ||
;; TODO : map that can take multiple lists | ||
(letrec ((map (lambda (f list) | ||
(if (eq list nil) | ||
nil | ||
(cons (f (car list)) | ||
(map f (cdr list))))))) | ||
(current-env)) | ||
|
||
;; filter : takes a predicate and a list and returns only the items | ||
;; that match the predicate | ||
(letrec ((filter (lambda (pred lst) | ||
(if lst | ||
(if (pred (car lst)) | ||
(cons (car lst) (filter pred (cdr lst))) | ||
(filter pred (cdr lst))))))) | ||
(current-env)) | ||
|
||
;; fold : a fold function over lists | ||
(letrec | ||
((fold (lambda (op accum lst) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For stylistic, consistency, let's use ACC for accumulator variable. I'd personally use L for lists also, but I feel less strongly about that. LST doesn't feel very Lispy. LIST would be better, but more verbose (hence my preference for L). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like > (let ((a (lambda () 2))
(a 1))
(a))
[6 iterations] => ERROR! lisp-1. So naming collision is a problem for LIST. I agree with L then. |
||
(if (car lst) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is treating NIL specially and will terminate the computation if a NIL is encountered in LST. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I think I mentioned earlier, NIL checks on the CAR of a list when recursing are usually a sign of confused logic (not an absolute rule, but it holds in this case). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, good point, I have to revisit a lot of the functions I wrote to correct this logic error 🤔 |
||
(fold op (op accum (car lst)) (cdr lst)) | ||
accum)))) | ||
(current-env)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an aside, I'm providing an example which implements this slightly differently, in a way that is more efficient for Lurk. We don't necessarily need to use this, but it may be instructive. I'll provide output showing the difference in iteration count so you see the point.
Compare with the cleaned-up version of the above code:
|
||
|
||
;; concat : concatenates two lists | ||
(let ((concat (lambda (a b) | ||
(fold (lambda (l i) (cons i l)) | ||
b | ||
(reverse a))))) | ||
(current-env)) | ||
|
||
;; drop : drops n items from the front of list l, returning the rest | ||
(letrec ((drop (lambda (n l) | ||
(if (= n 0) | ||
l | ||
(if l | ||
(drop (- n 1) (cdr l))))))) | ||
(current-env)) | ||
|
||
;; take : takes the first n items from the front of the list l | ||
(letrec ((take (lambda (n l) | ||
(if (= n 0) | ||
nil | ||
(if l | ||
(cons (car l) (take (- n 1) (cdr l)))))))) | ||
(current-env)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
eval-main : | ||
lurkrs safe-arith-test.lurk |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
!(:load "safe-arith.lurk") | ||
|
||
;; tests for neg | ||
!(:assert-eq 0 (neg 0)) | ||
!(:assert-eq 10 (neg (- 0 10))) | ||
!(:assert-eq (- 0 10) (neg 10)) | ||
|
||
;; tests for safe-add | ||
!(:assert-eq (+ 10 (neg 43)) (safe-add 10 (neg 43))) | ||
!(:assert-eq (+ (neg 111) 321) (safe-add (neg 111) 321)) | ||
!(:assert-eq (+ 11 5443225) (safe-add 11 5443225)) | ||
!(:assert-eq (+ (neg 5443225) (neg 43)) (safe-add (neg 5443225) (neg 43))) | ||
|
||
;; tests for safe-sub | ||
|
||
|
||
|
||
;; tests for safe-mult | ||
|
||
|
||
|
||
;; tests for floor-int-div | ||
|
||
|
||
|
||
;; tests for ceil-int-div | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should probably either leave these out, or name them something else. The "real"
and
andor
should be macros that have short-circuit logic (inor
, if the first arg evals to true, it immediately returns true without evaluating the 2nd arg).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds reasonable to me 👍