32
$\begingroup$

For example consider the following function:

Function[{u,v},u^2+v^4]

Is there anyway to define default values for the variables u and v?

I know that this is possible for ordinary functions as follows:

f[u_:1,v_:0]:=u^2+v^4

But I am looking for a way to do this with pure functions defined by Function[].

$\endgroup$
34
$\begingroup$

As far as I know there is no way to do this with the named parameter form of Function but you can use destructuring methods with SlotSequence (##):

f = {##} /. {u_: 1, v_: 0} :> body[u, v] &;

f[]
f[7]
f[7, 8]
body[1, 0]

body[7, 0]

body[7, 8]

It is possible to give your pure function Attributes using an undocumented form.
For Hold attributes you could use Hold or HoldComplete:

g = Function[Null, Hold[##] /. _[u_: 1, v_: 0] :> HoldForm[{u, v}], HoldAll];

g[1 + 1]
{1 + 1, 0}
$\endgroup$
  • 2
    $\begingroup$ I was thinking about the following construct: Function[{u, v}, u^2 + v^4 /. {u -> 1, v -> 1}] it is not the same as with optional arguments bacause one need to put coma: f[,]. Also, it seems to work well with Numeric arguments while for others ("a" for example), precedences are different. $\endgroup$ – Kuba Jul 26 '13 at 8:16
  • 2
    $\begingroup$ But if well understood it can be very useful (in case of many agruments) because you can specify which argument you want to take it's optional value. For example f[1,2,,2,,3], what is not so easy with standard optional arguments. $\endgroup$ – Kuba Jul 26 '13 at 8:23
  • 1
    $\begingroup$ @Kuba Okay, why don't you post that as an answer? It is an interesting interpretation of the problem. $\endgroup$ – Mr.Wizard Jul 26 '13 at 8:41
  • $\begingroup$ Could you tell something about this Null issue you have faced lately? $\endgroup$ – Kuba Jul 26 '13 at 9:19
7
$\begingroup$

I have left bottom part of this answer as a warning against not thinking :)

Here is my improvement. It is not so handy but it allows us to specify which argument has to take it's optional value.

edit - scoping.

f = Module[{x, y, z},
    Function[{u, v, g}, x^2 y^4 + z^4 /. {x -> (u /. Null -> 3), 
                                          y -> (v /. Null -> 2), 
                                          z -> g}] (*3rd arg has no opt. value*)
          ]

f[2, 1, 1]
f[, , 1]
f[1, , 1]
5

145

17

I do not know if it is worth the effort but one may want to scope x, y, z and make replacement more compact.


the warning :)

The following method will work only when the result of the function has different value than the value of one of arguments. I such case the result is will be replaced by it's optinal value.

Following examples are only lucky coincidence

This approach is not identical as standard optional arguments but it is interesting (but not working :) ).

f = Function[{u, v}, u^2 + v^4 /. {u -> 1, v -> 1}]

f[1,2]
17
f[,]
2

As you see, it is not like with standard optional arguments because you have to put comma inside. Thats because

f = Function[{u,v} ...]

$\endgroup$
  • $\begingroup$ The precedence here isn't that strange according to the output of f[,] // Trace, this also explains why something like f[a, 4] fails: the main procedure here is Null^2 + Null^4 /. {Null -> 1, Null -> 1}. $\endgroup$ – xzczd Jul 26 '13 at 8:52
  • $\begingroup$ @xzczd My first thouth was about this but then I checked f[1,4] and assumed that 4->1 will make an output 2. This is not happening so I'm confused. $\endgroup$ – Kuba Jul 26 '13 at 8:56
  • $\begingroup$ That's because ReplaceAll first evaluate the expression to be replaced before the replacement(ReplaceAll doesn't have Attributes like HoldFirst or HoldAll), so when the replacement begins, 1^2+4^4 has already evaluated to 257, so 4->1 won't work. $\endgroup$ – xzczd Jul 26 '13 at 9:04
  • $\begingroup$ f[1, 2 a] gives the expected result, (1+16 a^4), but f[1, a] gives (2) which is not the expected result. I don't know why this happens. $\endgroup$ – M6299 Jul 26 '13 at 9:07
  • 1
    $\begingroup$ @M6299 That's because when the replacement really begins, the expression has become 256 + 4 a^2 /. {2 a -> 1, 4 -> 1}, 4 a^2 doesn't match 2 a, so it remains as it is :) $\endgroup$ – xzczd Jul 26 '13 at 9:11
2
$\begingroup$

In this answer, I focus on the case of pure functions with one or more Slot in their body, which is different than the other answers.

First I present the function optionalFu which works just like Function, but which turns of a message. This message is the one that is generated when not all slots can be filled from the given input, like in {#,#2}&[5]. It is interesting to interpret this message as a warning, rather than an error and to ignore it, because Mathematica still fills all the slots it can. So {#,#2}&[5] evaluates to {5,#2} and it remains to be seen what to do with #2. The second function in this answer, optFuWithDefaults, replaces such slots (#2) with default values.

Be warned that the code for optionalFu and optFuWithDefaults are both very unreadable, but they appear to work nicely and it definitely more informative to read the examples below than the code.

optionalFu =
 Function[Null,
  ReplacePart[
   Function @@ Hold[
     Null,
     Quiet[## &, {Function::slotn}],
     HoldAll
     ]
   ,
   {2, 1, 0} -> Function @@ Hold[##]
   ], HoldAll]

optFuWithDefaults = 
 Function[{functionBody, slotDefaultRules, nSlots},
  Module[
   {fuToken, argsHeld},
   argsHeld = Hold @@ Slot /@ Range[nSlots] /. slotDefaultRules;
   ReplaceAll[
    Function[Null,
     (Function @@ {
         Null,
         fuToken[##],
         HoldAll
         }) @@ argsHeld, HoldAll]
    ,
    Join[
     {fuToken -> optionalFu @@ Hold @@ Unevaluated@functionBody},
     OwnValues@argsHeld
     ]
    ]
   ], HoldAll]

There are three arguments in this last function: {functionBody, slotDefaultRules, nSlots}.

functionBody is really a list (or you can use any head) of arguments that you would normally pass to Function. So if we set functionBody -> {{#, #2}} then this corresponds to {#, #2}&, while functionBody -> {Null, {#, Hold[#2]}, HoldAll} corresponds to Function[Null,{#, Hold[#2]}, HoldAll].

slotDefaultRules is a list of rules that sets default values for slots, if they are not present.

nSlots is the number of slots that are present in functionBody. I have chosen not to infer this from functionBody, because there may be functions inside functionBody and I don't want to mess with those. Instead, the user has to specify nSlots.

Simple example

fu1 = optFuWithDefaults[{{#, #2}}, {#2 -> 3}, 2];
fu1[1]
fu1[1, 2]
{1,3}
{1,2}

To quite some extent, functions in the functionBody argument are preserved.

fu2 = optFuWithDefaults[{{#,#2, {#,#2+5}&[#2,5]}}, {#2-> 3},2];
fu2[1]
fu2[1,2]
{1,3,{3,10}}
{1,2,{2,10}}

It also works with HoldAll and RuleDelayed

fu3 = optFuWithDefaults[
         {Null, {#, Hold[#2]}, HoldAll}, 
         {#2 :> Print[hello]}, 2];
fu3[1]
fu3[1, Print[goodbye]]
{1,Hold[Print[hello]]}
{1,Hold[Print[goodbye]]}

When using Rule and when not using HoldAll, arguments are also evaluated at appropriate times

fu4 = optFuWithDefaults[{{#, Hold[#2]}}, {#2 -> 2 + 2}, 2];
fu4[1]
fu4[1, 5 + 4]
{1,Hold[4]}
{1,Hold[9]}

As mentioned, you can use any head in the first argument (functionBody). This means you can also use Function and get a bit of syntax highlighting (but only in the first argument)

fu5 = optFuWithDefaults[
   {#, #2} &, {#2 -> 3}, 2];
fu5[1]
fu5[1, 2]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.