4

I'm trying to sort a list that looks something like this:

(defvar my-list '((:x 1 :y something) (:x 5 :y something) (:x 19 :y something)))

I'm trying to sort it by the value in :x. I could do that like this

(sort my-list #'> :key #'second)

but I would very much prefer to use the getf function instead of second, but I can't figure out how to pass :x as a parameter.

From what I can gather just #'getf returns (getf ((:x 1 :y something) '(:x 5 :y something) (:x 19 :y something)) [external]. How would I go about passing :x as the second parameter?

The only way I could think of is to create a wrapper-function for getf, which only takes a list as a parameter and passes in :x by default. There must be a better way though.

4
  • 3
    You don't need to quote the sublists inside a quoted list. Also, you shouldn't use destructive operations like SORT on quoted (literal) lists. You can use COPY-LIST (or COPY-TREE) to make a copy of the list before sorting. For the problem itself, making a wrapper function is the usual solution. That is commonly known as currying. The Alexandria library has functions CURRY and RCURRY for it. Commented Jul 24, 2017 at 14:22
  • @jkiiski, I don't know what happened there with quotes, you're completely right. As for the solution itself: you're probably right as well, but I'll hold off on declaring this solved, just in case there's a better (by which I mean more eloquent) solution. Commented Jul 24, 2017 at 14:37
  • 1
    One alternative would be to use structs (possibly with (:type list)), in which case you can use the accessor for the slot. Commented Jul 24, 2017 at 14:43
  • a short lambda provided by cl21 is rather handy IMO: (sort *my-list* #'> :key ^(getf % :x)). Commented Jul 25, 2017 at 11:32

2 Answers 2

8

If using a property as a key is common in your Lisp code, then you can define a function to create the key function. See the use of property-key-fn.

CL-USER 22 > (defparameter *my-list* (copy-list '((:x 1  :y foo)
                                                  (:x 5  :y bar)
                                                  (:x 19 :y baz))))
*MY-LIST*

CL-USER 23 > (defun property-key-fn (property)
               (lambda (plist)
                 (getf plist property)))
PROPERTY-KEY-FN

CL-USER 24 > (setf *my-list* (sort *my-list* #'> :key (property-key-fn :x)))
((:X 19 :Y BAZ) (:X 5 :Y BAR) (:X 1 :Y FOO))

CL-USER 25 > (setf *my-list* (sort *my-list* #'string> :key (property-key-fn :y)))
((:X 1 :Y FOO) (:X 19 :Y BAZ) (:X 5 :Y BAR))
Sign up to request clarification or add additional context in comments.

1 Comment

When I find myself doing this a lot, I use something like property-key-fn but shorten the name to =getf.
5

The is no better way than lambda:

(defvar *my-list* '((:x 1 :y something) (:x 5 :y something) (:x 19 :y something)))
(sort *my-list* #'> :key (lambda (plist) (getf plist :x)))
==> ((:X 19 :Y SOMETHING) (:X 5 :Y SOMETHING) (:X 1 :Y SOMETHING))

You might be looking for currying, but Common Lisp does not have that OOTB.

Rainer's answer offers ad hoc currying.

1 Comment

and a shorter version with cl21: (sort *my-list* #'> :key ^(getf % :x)) :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.