11
$\begingroup$

I want to make a custom function parameterNumber,which can return the parameter number of inbuilt function,as the Plot's documentation:

So the result of parameterNumber[Plot] is {{2}}

Or the RelationGraph's documentation: So the result of parameterNumber[RelationGraph] is {{2},{3}}

Or the Plus,Plus[arg1_,arg2_,arg3_,...] is valid,So the result of parameterNumber[Plus] is {0,Infinity} How to make a such function?


Status of current answer

The current method have little flaw still,Such as the MB1965's answer:

{#, paramNum[#]} & /@ {Plot, paramNum, Integrate, MemberQ, 
   Plot3D} // Column

Or the Mr.Wizard's answer:

{#,info[#]}&/@{Plot,Integrate,MemberQ,Plot3D}//Column


Related this post,but not very similar.And I think the Developer`CheckArgumentCount,Check,Internal`ProcessEquations`GetArguments or the ArgumentCountQ maybe can help this.

$\endgroup$
6
  • $\begingroup$ ?Plot produces a number of relevant pieces of information about Plot[]. One such piece of information is the different 'ways' you can call Plot[]. Perhaps you should look for a way to access the cells that are output from ?fun or ??func and then operate on their contents. Note that the formal number of different options for a function such as Plot[] is provided by Options[func] and in Plot's case there are 62 different options available. $\endgroup$
    – user42582
    Sep 2, 2016 at 15:05
  • $\begingroup$ Maybe you mean WolframLanguageData["Plot", EntityProperty["WolframLanguageSymbol", "PlaintextUsage"]],so I post another post $\endgroup$
    – yode
    Sep 2, 2016 at 15:09
  • 2
    $\begingroup$ Have you seen SyntaxInformation[]? $\endgroup$ Dec 16, 2016 at 19:51
  • $\begingroup$ @J.M. Yes,I have,but I fail to understant that string option that time,such as LocalVariables. $\endgroup$
    – yode
    Dec 16, 2016 at 23:59
  • $\begingroup$ @yode Can you specify exactly what, in your opinion, is insufficient or needs improvement in the existing answers? It would help to focus the question. $\endgroup$
    – MarcoB
    Feb 24, 2017 at 19:34

5 Answers 5

6
+500
$\begingroup$

Since you start from the perspective of documentation perhaps using the Symbol's usage message would be appropriate. I'll start with einbandi's usageString from Transform fancy usage messages in 1D string.

info[s_Symbol] :=
  With[
    {examples =
      StringCases[usageString[s], ToString[s] ~~ "[" ~~ Except["]"] ... ~~ "]"]},
    Length /@ Unevaluated @@@ ToHeldExpression @ examples // Union
  ]

I am sure it's not perfect but it may be a useful place to start. Intelligently combine this with J. M.'s implicit suggestion to use SyntaxInformation and you may have a fairly robust method.

$\endgroup$
11
  • $\begingroup$ Wow,thanks a lot.As this case, the info should give {0,1,2}.Actually I have considered that SyntaxInformation,I will try it again when I get an laptop. $\endgroup$
    – yode
    Dec 16, 2016 at 23:52
  • $\begingroup$ @yode I made two small changes to conform to that example. $\endgroup$
    – Mr.Wizard
    Dec 17, 2016 at 14:33
  • $\begingroup$ Wow,you made it...But sometimes little bug?Such as we cannot run info[Plot]?And I realize the answer should end with SyntaxInformation eventually maybe? $\endgroup$
    – yode
    Dec 17, 2016 at 16:22
  • $\begingroup$ If we cannot get a perfect solution,I will give the bounty to your this answer. :) $\endgroup$
    – yode
    Dec 17, 2016 at 16:25
  • $\begingroup$ @yode Like I said this is not perfect but hopefully it is still useful in some way. On my system (v10.1 under Windows) the updated code above returns {2} for info[Plot]. What are you experiencing? $\endgroup$
    – Mr.Wizard
    Dec 17, 2016 at 17:14
6
+150
$\begingroup$

Here's a (rather hacky but robust-ish) way to reduce patterns down to the number of arguments they occupy:

reduceSpans[spans_] :=
 # /. Except[_Symbol | _Span | _Integer | ∞] -> 1 & /@ spans //.
  {
   (m_ ;; n_ ;; l_) :> (m + n) ;; l,
   (Hold | List)[a___, PatternSequence[m_Integer, n_Integer], 
     b___] :> {a, m + n, b},
   (Hold | List)[a___, PatternSequence[m_Integer, n_ ;; k_], 
     b___] :> {a, (m + n) ;; k, b},
   (Hold | List)[a___, PatternSequence[m_ ;; k_, n_Integer], 
     b___] :> {a, m ;; (k + n), b},
   (Hold | List)[a___, PatternSequence[m_ ;; k_, n_ ;; j_], 
     b___] :> {a, (m + n) ;; (k + j), b}
   }
reducePatterns[p_, opsRep_: (0 ;; ∞)] :=
  p /. {
      _Symbol?(Function[s, 
          MatchQ[Unevaluated[s], 
           Except[Pattern | Optional | Blank | BlankSequence | 
             BlankNullSequence | PatternSequence | OptionsPattern]
           ], HoldFirst]) -> List
      } //. {
     Verbatim[Blank][___] -> 1,
     Verbatim[BlankSequence][___] -> (1 ;; ∞),
     Verbatim[BlankNullSequence][___] -> (0 ;; ∞),
     _OptionsPattern :> opsRep,
     Verbatim[HoldPattern][
        Verbatim[Pattern][a_, b_]
        ] | Verbatim[Pattern][a_, b_] :> b,
     Verbatim[PatternTest][a_, b_] :> a,
     Verbatim[Optional][a_, b_] :> (0 ;; a),
     Verbatim[Optional][a_] :> (0 ;; 1)
     } // reduceSpans;

This basically reduces the pattern down to simple pattern elements then adds the component numbers for those.

Then we can apply this to SyntaxInfo (with a little bit of hacking):

paramsBySInfo[sinfo_List, opsLen_: 0] :=

  With[{loc = Replace["LocalVariables" /. sinfo, _String -> {}], 
    arp = "ArgumentsPattern" /. sinfo},
   ParameterSequence @@
    Fold[
     Replace[#2,
       {
        {i_Integer, ___} :>
         If[
          Length@# < i,
          Replace[#, {
            {Span[n_, m_], r___} :>
             {Span[Max@{i, n}, Max@{i, m}], r}
            }],
          #
          ],
        _ :> #
        }] &,
     reducePatterns[arp, opsLen],
     loc
     ]
   ];

And for good measure apply it to DownValues too:

paramsByDVs[dvs_List, opsLen_: 0] :=

  With[{choices = 
     Replace[First@#, Verbatim[HoldPattern][_[a___]] :> Hold[a]] & /@ 
      dvs},
   ParameterSequence @@@ (reducePatterns[#, opsLen] &) /@ choices
   ];

Then write a wrapper that tries SyntaxInformation first, then DownValues:

paramNum[f_Symbol] :=
  Replace[
   SyntaxInformation@f, {
    {} :>
     Replace[
      DownValues[f], {
       {} -> 0,
       e_ :> paramsByDVs[e, Length@Options@f]
       }
      ],
    e_ :> paramsBySInfo[e, Length@Options@f]
    }
   ];

This gives pretty close to what you want I think:

In[375]:= paramNum /@ {Plot, paramNum, Integrate, MemberQ, Plot3D}

Out[375]= {
 ParameterSequence[2 ;; ∞], 
 {ParameterSequence[1]}, 
 ParameterSequence[2 ;; ∞], 
 ParameterSequence[1 ;; 3], 
 ParameterSequence[2 ;; ∞]
 }

Note that I supplied the number of options to my reduction functions, as that's the max number OptionsPattern[] should be able to take.

$\endgroup$
8
  • $\begingroup$ Thanks very very much,and we can just consider built-in function.,but there are some accidents in this code? :) $\endgroup$
    – yode
    Dec 20, 2016 at 0:07
  • $\begingroup$ Ah yeah, I didn't escape there no being a LocalVariables spec. I'll do that when I have Mathematica open again. First one I'll need to look at. $\endgroup$
    – b3m2a1
    Dec 20, 2016 at 0:42
  • $\begingroup$ @yode I cleaned those pieces, fixed up some stuff in adjusting for local variables, and added the max number of options for OptionsPattern[]. Hopefully this suits your needs better. $\endgroup$
    – b3m2a1
    Dec 20, 2016 at 7:02
  • $\begingroup$ But the paramNum[Plot] give ParameterSequence[2 ;; \[Infinity]]? $\endgroup$
    – yode
    Dec 20, 2016 at 7:40
  • $\begingroup$ That's because SynatxInformation@Plot gives {"ArgumentsPattern"->{___,OptionsPattern[]},"LocalVariables"->{Plot,{2}}}. The infinity comes from that first argument and the 2 comes from the "LocalVariables" spec. I figured where there are local variables you can assume there are at least that many arguments, but not necessarily that there will be no more arguments (excluding OptionsPatterns). If you are willing to make that assumption, when a "LocalVariables" spec is provided, all the information can come from that and the number of Options. $\endgroup$
    – b3m2a1
    Dec 20, 2016 at 13:58
4
$\begingroup$

an idea to work with:

len = 0;
Quiet[While[
   Check[Plot[Evaluate[Sequence @@ ConstantArray[0, {len}]]], 0,
     {Plot::argr, Plot::argrx}] == 0, ++len]];
len

2

I do not see how to readily generalize however

$\endgroup$
1
  • 1
    $\begingroup$ Thanks a lot!Maybe WolframLanguageData["RelationGraph", EntityProperty["WolframLanguageSymbol", "DocumentationExampleInputs"]] or WolframLanguageData["Plot", EntityProperty["WolframLanguageSymbol", "PlaintextUsage"]] can help a little. $\endgroup$
    – yode
    Sep 2, 2016 at 16:49
2
$\begingroup$

Here is a different approach that uses an information file that comes with Mathematica. For the moment, I will not turn the results into what you suggested. Instead, I will return the complete call pattern for all System` built-in functions. The number of arguments can be extracted from this call pattern if required.

functionInformation = 
  With[{file = 
     First[FileNames[
       "FunctionInformation.m", {$InstallationDirectory}, Infinity]]},
    Rule @@@ Get[file]];
info = Association @@ 
   Rule @@@ ("System`" /. functionInformation)[[All, {1, 2}]];

Now, you can use this Association to get the call pattern for all system functions:

info/@{"Plot","Integrate","MemberQ","Plot3D"}//Column

(*
{___,OptionsPattern[]}
{_,_,Optional[{__}],___,OptionsPattern[]}
{_,_.,_.,OptionsPattern[]}
{___,OptionsPattern[]}
*)

Edit

To create an output like in the question, one has to inspect each argument for a call pattern and create a {min,max} pair. For instance __ will create {1,Infinity}, while a single Blank gives {1,1}.

To make this consistent, we need to handle all possible patterns from this list:

Flatten[Values[info], 1] // DeleteDuplicates

Most cases can be handled together so that the final argLen function has only some definitions:

argLen[Verbatim[_] | Verbatim[Pattern][_, Verbatim[_]]] := {1, 1};
argLen[Verbatim[__] | Verbatim[Pattern][_, Verbatim[__]]] := {1, Infinity};
argLen[Verbatim[___] | Verbatim[Pattern][_, Verbatim[___]]] := {0, Infinity};
argLen[Verbatim[Optional][_]] := {0, 1};
argLen[Verbatim[OptionsPattern][]] := {0, Infinity};
argLen[_Symbol | _List | _String] := {1, 1};

The final paraNum function combines the min/max values for all arguments from a call pattern

paraNum[f_String] := ParameterSequence[
  Span @@ (Plus @@@ Transpose[argLen /@ info[f]])]

For your examples you get

{#,paraNum[#]}&/@{"Plot","Integrate","MemberQ","Plot3D"}//Column

(*
{Plot,ParameterSequence[0;;∞]}
{Integrate,ParameterSequence[2;;∞]}
{MemberQ,ParameterSequence[1;;∞]}
{Plot3D,ParameterSequence[0;;∞]} 
*)

Please note that MemberQ has an option, so your own answer is not correct. The call pattern for Plot3D is not given in detail and only says {___, OptionsPattern[]} which is wrong when you want to use it in this approach.

$\endgroup$
1
  • $\begingroup$ I find very hard to convert it into that suggestion result due to the existence of "LocalVariables".I give a try in following.If you have a better method.Let me know please. $\endgroup$
    – yode
    Feb 25, 2017 at 10:51
1
$\begingroup$

Almost,but not a perfect method.And since based on SyntaxInformation,which cannot adapt {DirectedEdges, EquirippleFilterKernel, NetTrain, ProcessEstimator,URLSaveAsynchronous}.So I give a specify value for it,respectively.

SetAttributes[paramNum, HoldFirst]
paramNum[NetTrain] := {{2}, {3}}
paramNum[URLSaveAsynchronous] := {{3}}
paramNum[EquirippleFilterKernel] := {{2}}
paramNum[fun_Symbol] := 
 Module[{pattern = SyntaxInformation[fun], loc, bankPattern, need, 
   option, range}, 
  If[StringContainsQ[ToString[Unevaluated[fun]], "$"], 
   "System variables", 
   If[SyntaxInformation[fun] === {} || 
     MemberQ[{ProcessEstimator, DirectedEdges}, fun], 
    "Option or Autoevaluating symbol", 
    bankPattern = 
     SequenceCases[
      "ArgumentsPattern" /. 
       SyntaxInformation[fun], {a___, ___OptionsPattern} :> a]; 
    need = Count[bankPattern, Verbatim[_] | _List]; 
    option = Count[bankPattern, Verbatim[_.] | _Optional]; 
    If[KeyMemberQ[pattern, "LocalVariables"] && 
      ListQ["LocalVariables" /. pattern], 
     loc = Max[Last["LocalVariables" /. pattern]]; 
     If[MemberQ[bankPattern, Verbatim[__]], 
      range = MinMax[{need + 1, Min[{Infinity, loc}], need + option}];
       If[AllTrue[range, NumericQ], List /@ Range @@ range, range], 
      If[MemberQ[bankPattern, Verbatim[___]], 
       range = MinMax[{need, Min[{Infinity, loc}], need + option}]; 
       If[AllTrue[range, NumericQ], List /@ Range @@ range, range], 
       If[AllTrue[MinMax[{need, loc, need + option}], NumericQ], 
        List /@ Range @@ MinMax[{need, loc, need + option}], 
        MinMax[{need, loc, need + option}]]]], 
     If[MemberQ[bankPattern, Verbatim[__]], {need + 1, Infinity}, 
      If[MemberQ[bankPattern, Verbatim[___]], {need, Infinity}, 
       List /@ Range[need, need + option]]]]]]]

Examples

Grid[{#, SyntaxInformation[#], paramNum[#]} & /@ 
  ToExpression[
   CanonicalName[RandomEntity["WolframLanguageSymbol", 10]]], 
 Frame -> All]

But I have to say the SyntaxInformation will give wrong information except {DirectedEdges, EquirippleFilterKernel, NetTrain,ProcessEstimator,URLSaveAsynchronous},which will result to a unexpected answer.Such as: Actually,the output of Plot should be {{2}},the output of FindCycle and FindClique should be {{1}, {2}, {3}}.

So I look forward a better solution.

$\endgroup$

Your Answer

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

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