Wojciech Gac
Wojciech Gac

Reputation: 1576

Inverting order of multiple values in Common Lisp

I've been thinking about the following problem. Suppose I'm dealing with a function returning multiple values, such as truncate. Is there a clever way to reverse the order of values that get returned? I'm talking about something more clever than e.g.

(multiple-value-bind (div rem) (truncate x y)
  (values rem div))

Upvotes: 1

Views: 132

Answers (3)

Diogo Franco
Diogo Franco

Reputation: 486

To have it as clean/consistent as multiple-value-bind, you could define a macro such as this:

(defmacro reverse-multiple-value-bind (args f &rest body) 
  `(multiple-value-bind ,(reverse args) 
      ,f 
      ,@body))

Then you have

>> (multiple-value-bind (x y) (floor 3.7) (print x) (print y))
3 
0.70000005

and

> (reverse-multiple-value-bind (x y) (floor 3.7) (print x) (print y))
0.70000005 
3 

Upvotes: 0

Kaz
Kaz

Reputation: 58588

This problem can be solved more cleverly by writing a higher order function whose input is a function that returns some (values a b), and which returns a function which calls that function, but returns (values b a). In other words a value reversing combinator:

(defun val-rev (function)
  (lambda (&rest args)
    (multiple-value-bind (a b) (apply function args)
       (values b a))))

Though inside the definition of this function we are doing the cumbersome thing you don't want (capturing the values with m-v-bind and reversing with values) this is encapsulated in the combinator and just an implementation detail. It's probably more efficient than consing up a value list and reversing it. Also, it specifically targets the first two values. If a function returns four values, A B C D, then reversing the multiple-value-list means that the first two return values will be C D. However, if we just bind the first two and reverse them, then we bet B A. Reversing the first two (or only two) values is clearly not the same as reversing all values.

Demo:

[1]> (truncate 17 3)
5 ;
2
[2]> (funcall (val-rev #'truncate) 17 3)
2 ;
5

Note that in a Lisp-1 dialect, the invocation loses the added noise of #' and funcall, reducing simply to: ((val-rev truncate) 17 3).

val-rev is kind of a dual of the flip higher order function which you see in some functional languages, which takes a binary function and returns a binary function which is that function, but with the arguments reversed.

Upvotes: 1

Tim Jasko
Tim Jasko

Reputation: 1542

I don't know how clever this is, but here's what you want:

(reverse (multiple-value-list (the-function-that-returns-multiple-values)))

multiple-value-list being the key, here.

To return these again as separate values, use values-list:

(values-list (reverse (multiple-value-list (the-function-that-returns-multiple-values))))

This whole page may be enlightening.

Upvotes: 5

Related Questions