12
$\begingroup$

I am writing a simple function return similar words.

Clear[similarWords]
similarWords[string_]:=Nearest[WordList[],string]

I want to add another argument n which is optional. When it presents, it controls the number of words returns.

Clear[similarWords]
similarWords[string_,n_:???]:=Nearest[WordList[],string,n]

But the problem is, what should I put it ???. I cannot figure it out.

The only way I can come up is

Clear[similarWords]
similarWords[string_,n_:-1]:=If[n==-1,Nearest[WordList[],string],Nearest[WordList[],string,n]]

Is there a neater way?

$\endgroup$
  • 3
    $\begingroup$ n___? or more precisely but longer: n:(_|PatternSequence[]). $\endgroup$ – Kuba Sep 9 '16 at 11:12
  • 2
    $\begingroup$ I might be not understanding something here, but why not just similarWords[string_, n_: 1] := Nearest[WordList[], string, n]? $\endgroup$ – corey979 Sep 9 '16 at 11:21
  • 1
    $\begingroup$ @corey979 Well, because by default Nearest return nearest items. But nearest words could be more than 1, if they have same distance $\endgroup$ – matheorem Sep 9 '16 at 11:24
  • 1
    $\begingroup$ Could you give an exemplary word that illustrates this? $\endgroup$ – corey979 Sep 9 '16 at 11:26
  • 1
    $\begingroup$ @corey979 you can try Nearest[WordList[], "suprise"] $\endgroup$ – matheorem Sep 9 '16 at 11:27
12
$\begingroup$

See @AlbertRetey's answer for all but trivial cases.


Don't use Optional, nor If. Use two definitions.

similarWords[string_] := Nearest[WordList[], string]

similarWords[string_, n_] := Nearest[WordList[], string, n]

I prefer this over just using arg___ and passing all arguments into Nearest because it keeps the responsibility for argument checking with similarWords. But of course just passing down everything is easier and quicker to write, and it's what I'd do in an interactive session (as opposed to a package or a situation where reusability and reliability is more important).

$\endgroup$
  • 1
    $\begingroup$ I was always wondering why there is no shorthand for (_|PatternSequence[]) $\endgroup$ – Kuba Sep 9 '16 at 12:01
  • 1
    $\begingroup$ @Kuba There's Repeated[..., {0, 1}], but it's not exactly short. $\endgroup$ – Szabolcs Sep 9 '16 at 12:06
  • $\begingroup$ Hi, @Kuba, so you just hand the opportunity over to Szabolcs? I am about to accpet this : ) $\endgroup$ – matheorem Sep 9 '16 at 12:08
  • $\begingroup$ @matheorem If you want to accept because it fits your needs better then it doesn't matter when I'm going to answer :) $\endgroup$ – Kuba Sep 9 '16 at 12:08
  • $\begingroup$ @Kuba Your solution is worth to be posted as an answer. I didn't know this way before and it is much more elegant than via Optional. $\endgroup$ – Alexey Popkov Sep 9 '16 at 12:53
10
$\begingroup$

for your simple example Szabolcs suggestions is certainly the best you can do. If for some reason in a less simple situation you want the behavior you described with just one definition this is what you could do:

similarWords[string_, n_: Automatic] := If[n === Automatic,
  Nearest[WordList[], string],
  Nearest[WordList[], string, n]
]

note that Automatic is just a symbol whose name is guaranteed to have no definition and seems to fit the intented behavior, it has no special functionality builtin. Technically you could just as well use any other "tag", including -1 as you suggested...

EDIT there has been some discussion if and when this or the two definition approach are to be prefered, and I think it is pretty clear that the two definition approach is best when it doesn't lead to code duplication. If it does, you might be better off with the single definition approach in this answer. Alternatively you could extract one or more functions which do what is common to both definitions and only call those and have the code which is different in the bodies of the two definitions...

$\endgroup$
  • $\begingroup$ +1. I also do exactly this in such cases, and prefer this over having 2 definitions. I also believe that this is the recommended solution, used most frequently in internal code / development. $\endgroup$ – Leonid Shifrin Sep 9 '16 at 13:25
  • $\begingroup$ @LeonidShifrin Ah, so my original solution is already as good as recommended :D, good to hear this. $\endgroup$ – matheorem Sep 9 '16 at 13:31
  • $\begingroup$ +1. One situation where this may make this more difficult is if you decide to extend the argument pattern in the future, e.g. add options too. $\endgroup$ – Szabolcs Sep 9 '16 at 13:34
  • $\begingroup$ @matheorem Feel free to change the accept. $\endgroup$ – Szabolcs Sep 9 '16 at 13:43
  • $\begingroup$ @Szabolcs I frequently use functions with both. What I do is of course to restrict the type of the optional arguments, like e.g. f[arg:Except[_?OptionQ]:Automatic, opts:OptionsPattern[]] (or stricter types when possible), and it works just fine. The reason I dislike two definition - based solution is code duplication and the need to maintain that (keep them in sync as the code changes). Over the time, more often than not such places become the origin of bad regression bugs. $\endgroup$ – Leonid Shifrin Sep 9 '16 at 14:35
9
$\begingroup$

I'd love to have a short syntax form for that. I'd use it more often:

similarWords[string_, n:(_|PatternSequence[]) ]:= Nearest[WordList[],string,n]
$\endgroup$
  • $\begingroup$ Hi, @Kuba. I just realized that n___ is not equivalent to n:(_|PatternSequence[]), and n:(_|PatternSequence[]) is actually equivalent to Szabolcs's suggestion, right? $\endgroup$ – matheorem Sep 9 '16 at 13:15
  • $\begingroup$ @matheorem correct. n___ alows you to put there a sequence which will break Nearest. Thus not included in my answer. $\endgroup$ – Kuba Sep 9 '16 at 13:16
  • $\begingroup$ Now, I truly understand. Your solution is as good as Szabolcs', even neater. Only one downside, it will confuse novice and make him(or her) look up the doc for quite a while : ) I wish I could accept both : ) $\endgroup$ – matheorem Sep 9 '16 at 13:22
  • $\begingroup$ @matheorem I'd go with Szabolcs for readability. $\endgroup$ – Kuba Sep 9 '16 at 13:24
7
$\begingroup$

This seems to work:

similarWords[string_, n: Repeated[_,{0,1}]] :=
  Nearest[WordList[], string, n]
$\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.