8

I'm developing a function f which should support optional arguments {x_, y_} in list form. A simple example looks like this:

f[{x_, y_}] := {x, y}

How can I set default values for the list argument {x_, y_}? My first attempt was

f[{x_:1, y_:2}] := {x, y}

which matches f[{}] and correctly returns {1, 2} but which does not match f[].

How can I define the function f so that f[] gives the desired default values?

Kuba
  • 136,707
  • 13
  • 279
  • 740
Robinaut
  • 1,245
  • 7
  • 18
  • http://reference.wolfram.com/language/tutorial/SettingUpFunctionsWithOptionalArguments.html – LLlAMnYP Apr 17 '15 at 13:35
  • add f[] = f[{}] later, it this ok? – Kuba Apr 29 '15 at 08:26
  • This seems not to be a nice solution. There are many internal MMA functions like ImageTake which use optinal and default arguments in list form. Basically I want to implement the same functionality regarding optional arguments as in ImageTake, see the documentation. – Robinaut Apr 29 '15 at 08:33
  • @Kuba Sorry for being unprecise. I'm just wondering if your solution is the "official" way to deal with optional list arguments like in ImageTake. I also tried f[args:{x_, y_}:{1,2}] := {x, y}which seems to work. However, I'm not sure if understand it the right way... – Robinaut Apr 29 '15 at 11:07
  • @Robinaut Use Needs["GeneralUtilities`"]; and evaluate PrintDefinitions@Cells, as you can see, WRI is not trying to put everything in one pattern either. It is not handy nor readable. – Kuba Apr 29 '15 at 11:14

2 Answers2

7

There are many ways to achieve this, I'd probably do something simple like

f[] = f[{}] 
(*set or set delayed, depends of context*)

But you can also do something more fancy, although it is not compact enough for me to like it :P

Default[f] = {};
f[Optional@{x_: 1, y_: 2}] := {x, y}

f[]
f[{}]
{1, 2}
{1, 2}
Kuba
  • 136,707
  • 13
  • 279
  • 740
3

Sorry for being unprecise. I'm just wondering if your solution is the "official" way to deal with optional list arguments like in ImageTake. I also tried f[args:{x_, y_}:{1,2}] := {x, y}which seems to work. However, I'm not sure if understand it the right way...

I do not believe I have seen a guideline or even a standard practice for this particular case. I quite like Kuba's solution and I often make use of that method, e.g.: Can I make a default for an optional argument the value of another argument?

You chose ImageTake as a model. For this function there is no need for the optional behavior that you describe as the syntax is already handled by Take. Note:

enter image description here

This syntax is valid for each argument of ImageTake (after the first) just as it is for Take. Therefore a trivial reimplementation is:

myImageTake[i : _Image | _Image3D, spec__] := 
  Head[i][Take[ImageData[i], spec], ColorSpace -> ImageColorSpace[i]]

Test:

img = Import["http://cdn.sstatic.net/mathematica/img/logo@2.png"]  (* site logo *)

myImageTake[img, -66, {30, 120}]
ImageTake[img, -66, {30, 120}]

enter image description here

img3D = ExampleData[{"TestImage3D", "CThead"}];

myImageTake[img3D, -20, {80, 180}, 120]
ImageTake[img3D, -20, {80, 180}, 120]

enter image description here

If I wanted tighter control of the input than spec__ I would use something like:

p1 = (All | _Integer | {Repeated[_Integer, {1, 3}]})?(FreeQ[#, 0] &);

ClearAll[myImageTake]

myImageTake[i : _Image | _Image3D, spec : p1 ..] /; 
  Length[{spec}] <= Length@ImageDimensions[i] := 
    Head[i][Take[ImageData[i], spec], ColorSpace -> ImageColorSpace[i]]

This example may be a bit too specific but it is the one that you gave. For other examples the pattern you found (args:{x_, y_}:{1,2}) or the method of passing arguments back to the function (Kuba's answer and my linked post above) may be more appropriate. If you give another example or model function I shall try to address that as well.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371