Lisp/Intersection of Lists
Hello i am trying to create a function in common-lisp that takes two lists, and output their intersections, assuming there is no repetition in each list without using intersection function. It seems that it is not working. Can anyone help?
(defun isect (lst_1 lst_2)
(setq newlist nil)
(dolist (x lst_1 newlist)
(dolist (y lst_2)
(if (equal x y) (setf newlist (append newlist x)))
)
)
)
lisp common-lisp set-intersection
add a comment |
Hello i am trying to create a function in common-lisp that takes two lists, and output their intersections, assuming there is no repetition in each list without using intersection function. It seems that it is not working. Can anyone help?
(defun isect (lst_1 lst_2)
(setq newlist nil)
(dolist (x lst_1 newlist)
(dolist (y lst_2)
(if (equal x y) (setf newlist (append newlist x)))
)
)
)
lisp common-lisp set-intersection
2
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
1
Your function unnecessarily refers to a free variable. The variablenewlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.
– Kaz
Nov 8 '18 at 21:34
1
^ ... I.e. your(setq newlist nil)
is doing the same thing as(setf (symbol-value 'newlist) nil)
.
– Kaz
Nov 8 '18 at 21:35
1
^ and this is "bad" because multiple unrelated functions in the Lisp image could usenewlist
in the same way, leading to bugs. Another function that usesnewlist
might malfunction due to the surprise that a call toisect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.
– Kaz
Nov 8 '18 at 21:38
add a comment |
Hello i am trying to create a function in common-lisp that takes two lists, and output their intersections, assuming there is no repetition in each list without using intersection function. It seems that it is not working. Can anyone help?
(defun isect (lst_1 lst_2)
(setq newlist nil)
(dolist (x lst_1 newlist)
(dolist (y lst_2)
(if (equal x y) (setf newlist (append newlist x)))
)
)
)
lisp common-lisp set-intersection
Hello i am trying to create a function in common-lisp that takes two lists, and output their intersections, assuming there is no repetition in each list without using intersection function. It seems that it is not working. Can anyone help?
(defun isect (lst_1 lst_2)
(setq newlist nil)
(dolist (x lst_1 newlist)
(dolist (y lst_2)
(if (equal x y) (setf newlist (append newlist x)))
)
)
)
lisp common-lisp set-intersection
lisp common-lisp set-intersection
edited Nov 8 '18 at 14:29
Alper Oguzkan
asked Nov 8 '18 at 13:59
Alper OguzkanAlper Oguzkan
133
133
2
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
1
Your function unnecessarily refers to a free variable. The variablenewlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.
– Kaz
Nov 8 '18 at 21:34
1
^ ... I.e. your(setq newlist nil)
is doing the same thing as(setf (symbol-value 'newlist) nil)
.
– Kaz
Nov 8 '18 at 21:35
1
^ and this is "bad" because multiple unrelated functions in the Lisp image could usenewlist
in the same way, leading to bugs. Another function that usesnewlist
might malfunction due to the surprise that a call toisect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.
– Kaz
Nov 8 '18 at 21:38
add a comment |
2
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
1
Your function unnecessarily refers to a free variable. The variablenewlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.
– Kaz
Nov 8 '18 at 21:34
1
^ ... I.e. your(setq newlist nil)
is doing the same thing as(setf (symbol-value 'newlist) nil)
.
– Kaz
Nov 8 '18 at 21:35
1
^ and this is "bad" because multiple unrelated functions in the Lisp image could usenewlist
in the same way, leading to bugs. Another function that usesnewlist
might malfunction due to the surprise that a call toisect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.
– Kaz
Nov 8 '18 at 21:38
2
2
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
1
1
Your function unnecessarily refers to a free variable. The variable
newlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.– Kaz
Nov 8 '18 at 21:34
Your function unnecessarily refers to a free variable. The variable
newlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.– Kaz
Nov 8 '18 at 21:34
1
1
^ ... I.e. your
(setq newlist nil)
is doing the same thing as (setf (symbol-value 'newlist) nil)
.– Kaz
Nov 8 '18 at 21:35
^ ... I.e. your
(setq newlist nil)
is doing the same thing as (setf (symbol-value 'newlist) nil)
.– Kaz
Nov 8 '18 at 21:35
1
1
^ and this is "bad" because multiple unrelated functions in the Lisp image could use
newlist
in the same way, leading to bugs. Another function that uses newlist
might malfunction due to the surprise that a call to isect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.– Kaz
Nov 8 '18 at 21:38
^ and this is "bad" because multiple unrelated functions in the Lisp image could use
newlist
in the same way, leading to bugs. Another function that uses newlist
might malfunction due to the surprise that a call to isect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.– Kaz
Nov 8 '18 at 21:38
add a comment |
4 Answers
4
active
oldest
votes
I assume isect
with both arguments being the same list should return an equal list and not one that is flattened. In that case (append newlist x)
is not adding an element to the end of a list. Here is my suggestion:
(defun intersect (lst-a lst-b &aux result)
(dolist (a lst-a (nreverse result))
(dolist (b lst-b)
(when (equal a b)
(push a result)))))
This is O(n^2) while you can do it in O(n) using a hash table.
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
add a comment |
If you can ensure that the lists are sorted (ascending) you could do something like
(defun isect (l1 l2 acc)
(let ((f1 (car l1))
(f2 (car l2))
(r1 (cdr l1))
(r2 (cdr l2)))
(cond ((or (null l1) (null l2)) acc)
((= f1 f2) (isect r1 r2 (cons f1 acc)))
((< f1 f2) (isect r1 l2 acc))
((> f1 f2) (isect l1 r2 acc)))))
Note though, that the result is in reversed order. Also, the example assumes that the
elements are numbers. If you wanted to generalize, you could pass an ordering as an optional argument to make it work with arbitrary elements.
NB: A solution using loop
would likely be faster but I could not think of how to partially "advance" the lists when the car
s are different.
add a comment |
A built-in way (that won't work for homeworks ;) ) is to use intersection
: https://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists
What elements are both in list-a and list-b ?
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
add a comment |
;; the key function for simple lists
(defun id (x) x)
;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;; default is #'eql (for simple numbers or symbols this is sufficient)
;; - for numbers only #'=
;; - for characters only #'char=
;; - for strings only #'string=
;; - for lists #'equal
;; - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number: #'<
;; - decreasing number: #'>
;; - increasing char: #'char<
;; - decreasing char: #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;; (one could sort by length of element etc.)
;; so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;; so, how withing each element the to-be-sorted element should be accessed
;; for this, I prepared the `id` - identity - function because this is the
;; sort-key when simple comparison of the elements of the two lists
;; should be compared - and this function is also used for testing
;; for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
(let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
(lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
(labels ((.isect (l1 l2 acc)
(cond ((or (null l1) (null l2)) (nreverse acc))
(t (let ((l1-element (funcall sort-key (car l1)))
(l2-element (funcall sort-key (car l2))))
(cond ((funcall sort-test l1-element l2-element)
(.isect (cdr l1) l2 acc))
((funcall equality-test l1-element l2-element)
(.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
(t (.isect l1 (cdr l2) acc))))))))
(.isect lst-1-sorted lst-2-sorted '()))))
Simple tests:
(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)
(isect '(#a #c #h #t #e #r #b #a #h #n)
'(#a #m #s #e #l #s #t #a #r)
:equality-test #'char=
:sort-test #'char<
:key #'id)
;; => (#a #a #e #r #t)
(isect '("this" "is" "just" "a" "boring" "test")
'("this" "boring" "strings" "are" "to" "be" "intersected")
:equality-test #'string=
:sort-test #'string<
:key #'id)
;; => ("boring" "this")
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53209247%2flisp-intersection-of-lists%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
I assume isect
with both arguments being the same list should return an equal list and not one that is flattened. In that case (append newlist x)
is not adding an element to the end of a list. Here is my suggestion:
(defun intersect (lst-a lst-b &aux result)
(dolist (a lst-a (nreverse result))
(dolist (b lst-b)
(when (equal a b)
(push a result)))))
This is O(n^2) while you can do it in O(n) using a hash table.
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
add a comment |
I assume isect
with both arguments being the same list should return an equal list and not one that is flattened. In that case (append newlist x)
is not adding an element to the end of a list. Here is my suggestion:
(defun intersect (lst-a lst-b &aux result)
(dolist (a lst-a (nreverse result))
(dolist (b lst-b)
(when (equal a b)
(push a result)))))
This is O(n^2) while you can do it in O(n) using a hash table.
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
add a comment |
I assume isect
with both arguments being the same list should return an equal list and not one that is flattened. In that case (append newlist x)
is not adding an element to the end of a list. Here is my suggestion:
(defun intersect (lst-a lst-b &aux result)
(dolist (a lst-a (nreverse result))
(dolist (b lst-b)
(when (equal a b)
(push a result)))))
This is O(n^2) while you can do it in O(n) using a hash table.
I assume isect
with both arguments being the same list should return an equal list and not one that is flattened. In that case (append newlist x)
is not adding an element to the end of a list. Here is my suggestion:
(defun intersect (lst-a lst-b &aux result)
(dolist (a lst-a (nreverse result))
(dolist (b lst-b)
(when (equal a b)
(push a result)))))
This is O(n^2) while you can do it in O(n) using a hash table.
answered Nov 8 '18 at 15:35
SylwesterSylwester
34.4k22955
34.4k22955
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
add a comment |
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
I am newbie to Lisp, yeah i realized that append does not change the original list, i have re-defined my function using push macro. Thanks further.
– Alper Oguzkan
Nov 13 '18 at 14:21
add a comment |
If you can ensure that the lists are sorted (ascending) you could do something like
(defun isect (l1 l2 acc)
(let ((f1 (car l1))
(f2 (car l2))
(r1 (cdr l1))
(r2 (cdr l2)))
(cond ((or (null l1) (null l2)) acc)
((= f1 f2) (isect r1 r2 (cons f1 acc)))
((< f1 f2) (isect r1 l2 acc))
((> f1 f2) (isect l1 r2 acc)))))
Note though, that the result is in reversed order. Also, the example assumes that the
elements are numbers. If you wanted to generalize, you could pass an ordering as an optional argument to make it work with arbitrary elements.
NB: A solution using loop
would likely be faster but I could not think of how to partially "advance" the lists when the car
s are different.
add a comment |
If you can ensure that the lists are sorted (ascending) you could do something like
(defun isect (l1 l2 acc)
(let ((f1 (car l1))
(f2 (car l2))
(r1 (cdr l1))
(r2 (cdr l2)))
(cond ((or (null l1) (null l2)) acc)
((= f1 f2) (isect r1 r2 (cons f1 acc)))
((< f1 f2) (isect r1 l2 acc))
((> f1 f2) (isect l1 r2 acc)))))
Note though, that the result is in reversed order. Also, the example assumes that the
elements are numbers. If you wanted to generalize, you could pass an ordering as an optional argument to make it work with arbitrary elements.
NB: A solution using loop
would likely be faster but I could not think of how to partially "advance" the lists when the car
s are different.
add a comment |
If you can ensure that the lists are sorted (ascending) you could do something like
(defun isect (l1 l2 acc)
(let ((f1 (car l1))
(f2 (car l2))
(r1 (cdr l1))
(r2 (cdr l2)))
(cond ((or (null l1) (null l2)) acc)
((= f1 f2) (isect r1 r2 (cons f1 acc)))
((< f1 f2) (isect r1 l2 acc))
((> f1 f2) (isect l1 r2 acc)))))
Note though, that the result is in reversed order. Also, the example assumes that the
elements are numbers. If you wanted to generalize, you could pass an ordering as an optional argument to make it work with arbitrary elements.
NB: A solution using loop
would likely be faster but I could not think of how to partially "advance" the lists when the car
s are different.
If you can ensure that the lists are sorted (ascending) you could do something like
(defun isect (l1 l2 acc)
(let ((f1 (car l1))
(f2 (car l2))
(r1 (cdr l1))
(r2 (cdr l2)))
(cond ((or (null l1) (null l2)) acc)
((= f1 f2) (isect r1 r2 (cons f1 acc)))
((< f1 f2) (isect r1 l2 acc))
((> f1 f2) (isect l1 r2 acc)))))
Note though, that the result is in reversed order. Also, the example assumes that the
elements are numbers. If you wanted to generalize, you could pass an ordering as an optional argument to make it work with arbitrary elements.
NB: A solution using loop
would likely be faster but I could not think of how to partially "advance" the lists when the car
s are different.
edited Nov 13 '18 at 21:05
answered Nov 9 '18 at 16:35
AroobAroob
467
467
add a comment |
add a comment |
A built-in way (that won't work for homeworks ;) ) is to use intersection
: https://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists
What elements are both in list-a and list-b ?
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
add a comment |
A built-in way (that won't work for homeworks ;) ) is to use intersection
: https://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists
What elements are both in list-a and list-b ?
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
add a comment |
A built-in way (that won't work for homeworks ;) ) is to use intersection
: https://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists
What elements are both in list-a and list-b ?
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
A built-in way (that won't work for homeworks ;) ) is to use intersection
: https://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists
What elements are both in list-a and list-b ?
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
answered Nov 14 '18 at 11:59
EhvinceEhvince
8,57042652
8,57042652
add a comment |
add a comment |
;; the key function for simple lists
(defun id (x) x)
;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;; default is #'eql (for simple numbers or symbols this is sufficient)
;; - for numbers only #'=
;; - for characters only #'char=
;; - for strings only #'string=
;; - for lists #'equal
;; - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number: #'<
;; - decreasing number: #'>
;; - increasing char: #'char<
;; - decreasing char: #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;; (one could sort by length of element etc.)
;; so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;; so, how withing each element the to-be-sorted element should be accessed
;; for this, I prepared the `id` - identity - function because this is the
;; sort-key when simple comparison of the elements of the two lists
;; should be compared - and this function is also used for testing
;; for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
(let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
(lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
(labels ((.isect (l1 l2 acc)
(cond ((or (null l1) (null l2)) (nreverse acc))
(t (let ((l1-element (funcall sort-key (car l1)))
(l2-element (funcall sort-key (car l2))))
(cond ((funcall sort-test l1-element l2-element)
(.isect (cdr l1) l2 acc))
((funcall equality-test l1-element l2-element)
(.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
(t (.isect l1 (cdr l2) acc))))))))
(.isect lst-1-sorted lst-2-sorted '()))))
Simple tests:
(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)
(isect '(#a #c #h #t #e #r #b #a #h #n)
'(#a #m #s #e #l #s #t #a #r)
:equality-test #'char=
:sort-test #'char<
:key #'id)
;; => (#a #a #e #r #t)
(isect '("this" "is" "just" "a" "boring" "test")
'("this" "boring" "strings" "are" "to" "be" "intersected")
:equality-test #'string=
:sort-test #'string<
:key #'id)
;; => ("boring" "this")
add a comment |
;; the key function for simple lists
(defun id (x) x)
;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;; default is #'eql (for simple numbers or symbols this is sufficient)
;; - for numbers only #'=
;; - for characters only #'char=
;; - for strings only #'string=
;; - for lists #'equal
;; - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number: #'<
;; - decreasing number: #'>
;; - increasing char: #'char<
;; - decreasing char: #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;; (one could sort by length of element etc.)
;; so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;; so, how withing each element the to-be-sorted element should be accessed
;; for this, I prepared the `id` - identity - function because this is the
;; sort-key when simple comparison of the elements of the two lists
;; should be compared - and this function is also used for testing
;; for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
(let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
(lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
(labels ((.isect (l1 l2 acc)
(cond ((or (null l1) (null l2)) (nreverse acc))
(t (let ((l1-element (funcall sort-key (car l1)))
(l2-element (funcall sort-key (car l2))))
(cond ((funcall sort-test l1-element l2-element)
(.isect (cdr l1) l2 acc))
((funcall equality-test l1-element l2-element)
(.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
(t (.isect l1 (cdr l2) acc))))))))
(.isect lst-1-sorted lst-2-sorted '()))))
Simple tests:
(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)
(isect '(#a #c #h #t #e #r #b #a #h #n)
'(#a #m #s #e #l #s #t #a #r)
:equality-test #'char=
:sort-test #'char<
:key #'id)
;; => (#a #a #e #r #t)
(isect '("this" "is" "just" "a" "boring" "test")
'("this" "boring" "strings" "are" "to" "be" "intersected")
:equality-test #'string=
:sort-test #'string<
:key #'id)
;; => ("boring" "this")
add a comment |
;; the key function for simple lists
(defun id (x) x)
;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;; default is #'eql (for simple numbers or symbols this is sufficient)
;; - for numbers only #'=
;; - for characters only #'char=
;; - for strings only #'string=
;; - for lists #'equal
;; - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number: #'<
;; - decreasing number: #'>
;; - increasing char: #'char<
;; - decreasing char: #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;; (one could sort by length of element etc.)
;; so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;; so, how withing each element the to-be-sorted element should be accessed
;; for this, I prepared the `id` - identity - function because this is the
;; sort-key when simple comparison of the elements of the two lists
;; should be compared - and this function is also used for testing
;; for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
(let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
(lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
(labels ((.isect (l1 l2 acc)
(cond ((or (null l1) (null l2)) (nreverse acc))
(t (let ((l1-element (funcall sort-key (car l1)))
(l2-element (funcall sort-key (car l2))))
(cond ((funcall sort-test l1-element l2-element)
(.isect (cdr l1) l2 acc))
((funcall equality-test l1-element l2-element)
(.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
(t (.isect l1 (cdr l2) acc))))))))
(.isect lst-1-sorted lst-2-sorted '()))))
Simple tests:
(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)
(isect '(#a #c #h #t #e #r #b #a #h #n)
'(#a #m #s #e #l #s #t #a #r)
:equality-test #'char=
:sort-test #'char<
:key #'id)
;; => (#a #a #e #r #t)
(isect '("this" "is" "just" "a" "boring" "test")
'("this" "boring" "strings" "are" "to" "be" "intersected")
:equality-test #'string=
:sort-test #'string<
:key #'id)
;; => ("boring" "this")
;; the key function for simple lists
(defun id (x) x)
;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;; default is #'eql (for simple numbers or symbols this is sufficient)
;; - for numbers only #'=
;; - for characters only #'char=
;; - for strings only #'string=
;; - for lists #'equal
;; - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number: #'<
;; - decreasing number: #'>
;; - increasing char: #'char<
;; - decreasing char: #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;; (one could sort by length of element etc.)
;; so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;; so, how withing each element the to-be-sorted element should be accessed
;; for this, I prepared the `id` - identity - function because this is the
;; sort-key when simple comparison of the elements of the two lists
;; should be compared - and this function is also used for testing
;; for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
(let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
(lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
(labels ((.isect (l1 l2 acc)
(cond ((or (null l1) (null l2)) (nreverse acc))
(t (let ((l1-element (funcall sort-key (car l1)))
(l2-element (funcall sort-key (car l2))))
(cond ((funcall sort-test l1-element l2-element)
(.isect (cdr l1) l2 acc))
((funcall equality-test l1-element l2-element)
(.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
(t (.isect l1 (cdr l2) acc))))))))
(.isect lst-1-sorted lst-2-sorted '()))))
Simple tests:
(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)
(isect '(#a #c #h #t #e #r #b #a #h #n)
'(#a #m #s #e #l #s #t #a #r)
:equality-test #'char=
:sort-test #'char<
:key #'id)
;; => (#a #a #e #r #t)
(isect '("this" "is" "just" "a" "boring" "test")
'("this" "boring" "strings" "are" "to" "be" "intersected")
:equality-test #'string=
:sort-test #'string<
:key #'id)
;; => ("boring" "this")
answered Nov 16 '18 at 6:32
Gwang-Jin KimGwang-Jin Kim
2,416116
2,416116
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53209247%2flisp-intersection-of-lists%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
"It seems that it is not working." Please add error messages, if you have any
– coredump
Nov 8 '18 at 15:10
1
Your function unnecessarily refers to a free variable. The variable
newlist
has no binding in the function; it is global. Lisp isn't Python; assigning to a symbol doesn't create a local variable. It refers to an existing variable, and if one hasn't been defined, then it refers to the symbol's global value cell.– Kaz
Nov 8 '18 at 21:34
1
^ ... I.e. your
(setq newlist nil)
is doing the same thing as(setf (symbol-value 'newlist) nil)
.– Kaz
Nov 8 '18 at 21:35
1
^ and this is "bad" because multiple unrelated functions in the Lisp image could use
newlist
in the same way, leading to bugs. Another function that usesnewlist
might malfunction due to the surprise that a call toisect
overwrote the variable. Deliberate, purposeful, organized global variables are already fairly bad in software; accidental global variables are worse.– Kaz
Nov 8 '18 at 21:38