Kara-Moon Forum
March 29, 2024, 12:19:50 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
News: You can go back to the main site here: Kara-Moon site
 
   Home   Help Search Login Register  
Pages: [1] 2
  Print  
Author Topic: Omitted parameters in a subroutine call  (Read 5095 times)
sciurius
Sr. Member
****
Posts: 443



« on: December 07, 2020, 09:53:30 AM »

I want a subroutine that can be called with or without argument.

I can do this like:

Code:
DefCall Foo Arg=__OMITTED__
  If Ne $$Arg __OMITTED__
   ...called with argument...
  Else
   ...called without argument...
  Endif
EndDefCall

but this is not very elegant. What I would like is something like this:

Code:
DefCall Foo Arg
  If Def Arg
   ...called with argument...
  Else
   ...called without argument...
  Endif
EndDefCall

What do you think?
« Last Edit: December 07, 2020, 10:06:44 AM by sciurius » Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #1 on: December 07, 2020, 04:54:16 PM »

Let me look at the mechanics of subroutine before I give an answer Smiley

If I was doing this in my own code I'd probably just create 2 stubs, one for args and the other without and have both of them call the worker Smiley
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #2 on: December 08, 2020, 01:17:14 PM »

The attached patch allows subroutines to omit parameters even if there is no default. These will test as 'not defined' in the subroutine body.

Code:
DefCall Example Chords
  IF Def Chords
    print Chords: $Chords
  Else
    print No Chords
  EndIf
EndDefCall

Call Example C // will print "Chords: C"
Call Example // will print "No Chords"

Set Chords Canary
Call Example C // will print "Chords: C"

print $Chords is still Canary

Call Example // will print "No Chords"

print $Chords is still Canary

mma -e will reveal the magic...

* func.patch.txt (1.78 KB - downloaded 151 times.)
Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #3 on: December 10, 2020, 01:50:40 AM »

I went to apply this patch but it appears to be truncated. Could you double check.
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #4 on: December 10, 2020, 07:27:13 AM »

Strange... It looks okay and patches ok here.
Can you try https://www.squirrel.nl/pub/xfer/uploads/3Ci1tRc05tWVmA9Q5zUAUG1w.patch.txt ?
MD5 checksum of the patch is c743b26dc54f41dd6c47ca0345e31374 .
Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #5 on: December 10, 2020, 06:39:03 PM »


Okay, got it working. I'd already modified my func.py to test some things ... and patch didn't appreciate that Smiley
Logged

My online life: http://www.mellowood.ca
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #6 on: December 10, 2020, 07:21:03 PM »

My problem with this method is that variables defined outside of the function call are still valid. So,

   DefCall test A B C
     Print $C
   EndDefCall

and then the mma code:

   Set C Something
   Call Test  1, 2

will still print out "Something".

I just see a nightmare debugging session trying to figure out why a script is doing goofy things.

And easier handling is to modify one line of code at line 173:

Code:
        if d is None:
             #error("Call: '%s' has no default for '%s'." % (fname, p))
             d="UNDEFINED"
        callParams.append(d)
   
Now, any variables not passed in the call will receive a default value "UNDEFINED". I don't particularly love this, or the value, but is this workable?
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #7 on: December 11, 2020, 08:45:56 AM »

I don't understand...

Code:
DefCall test A B C
     Print $C
EndDefCall

Set C Something
Call Test  1, 2

This prints: Call: Function 'TEST' needs 1 params, '2' given.
(Due to the missing commas between A and B and C.)
When this is fixed:

Code:
DefCall test A, B, C
     Print $C
EndDefCall

Set C Something
Call Test  1, 2

This prints User variable 'C'  has not been defined
Exactly as it should...

This is 20.12 with my patch applied. Maybe you changed anything else in MMA/funcs.py?
Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #8 on: December 11, 2020, 04:27:34 PM »

Did you try it with my one line patch and your patch NOT applied? For me it works just fine:

I copied your mma script and I get the one line output of UNDEFINED.

I really think, having thought about it more, that we should enforce the rule that if you create a function with X params you need to supply 3 params ... UNLESS some of those params have been supplied with default values. So, it simply becomes

Code:
DefCall test A, B=Var2, C=Var3
     Print $C
EndDefCall

And you can call it with 1, 2 or 3 params. Testing for number of params is simple: just check for the default values.
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #9 on: December 11, 2020, 06:22:11 PM »

Your one-line patch works, but setting an omitted parameter to some arbitrary value like 'UNDEFINED' is ugly and error prone.

Setting it to None is much more elegant, and can be checked in the subroutine with a simple "If NDef ...".
It is strange that my patch doesn't seem to work for you.
Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #10 on: December 11, 2020, 06:47:52 PM »

The patch did work. But, I don't like the way previously defined vars are handled. Plus, most languages enforce the rule that a function call needs the same number of params as the function definition ... EXCEPT when you have supplied default values. So, again, with your original example you just have to create the function with 1 or 2 default value functions ...

Now, it might be worthwhile discussing a method to provide NULL values in a default value. But, then, even if we do this, I'm not sure how we'd get MMA's if/then stuff to easily deal with it.

Yes, having to check against a arbitrary string value is ugly ... I'm just not sure of an easier way. Getting back to your original example:

Code:
DefCall Foo Arg=__OMITTED__
  If Ne $$Arg __OMITTED__
   ...called with argument...
  Else
   ...called without argument...
  Endif
EndDefCall

Would changing the initial test to "If eq Arg __OMITTED__" be that much uglier than "If def A". Hint, you don't need "$$". Even more elegant, maybe!, is the code in egs/subroutines/convert4to5.mma which uses an array?

Try this with a real language like python ...

Code:
def foo(a,b,d):
 print(a)

foo(a)

and you'll see the same behavior .. unless you do something like "def foo(a,b=None, c=None)" in which case we're down to my point.
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #11 on: December 11, 2020, 09:16:42 PM »

Well, the basic problem is that MMA does not have the concept of "no value" or "empty". It does have "not defined" (not set), so I tried to use that.

BTW I use this __OMITTED__ thing in my FPP plugin. Since it is in a dynamically generated subroutine the user doesn't see it unless she turns on debugging.

Quote
Now, it might be worthwhile discussing a method to provide NULL values in a default value. But, then, even if we do this, I'm not sure how we'd get MMA's if/then stuff to easily deal with it.

Using "If NDef ..." comes to mind  Grin.
Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #12 on: December 11, 2020, 10:08:39 PM »

Yes, we do have ndef ... but it returns TRUE no matter what value the variable is set to. That's cause it's looking to see if the variable exists, not if it has a value. So,

Code:
Set foo 123
Set woof

both foo and woof are TRUE. I think some test saying "has content" or "is not empty" might be a good addition?
Logged

My online life: http://www.mellowood.ca
sciurius
Sr. Member
****
Posts: 443



« Reply #13 on: December 12, 2020, 02:07:19 PM »

And what may constitue "empty"?

Initially, variable X does not exist. This can be tested with "If Def" (defined) "If NDef" (not defined).

If it has a value, it will always test defined.

The statement "Set X" will set X to an empty value. This is the only way to set an empty value. In particular you cannot set empty as default (Default: Requires two arguments). You cannot pass it as a named argument (Call Foo arg=). Also, you cannot test for a variable having and empty value except by comparing it to another empty variable. BTW, handling empty vaues would be easy if MMA parser would deal with quoted strings.

Summarizing: If there is a need to detect omitted parameters in a subroutine call, defaulting the parameter to a weird value works. Setting the default value automatically to something like "UNDEFINED" (your one-line patch) does not really add value and may add confusion.

My patch is slightly more complex but has the benefit of using real undefined (not set) value.

I don't think that being able to omit call parameters is a big issue so it is probably better to not implement solutions that to not feel quite well.

Logged
bvdp
Kara-Moon Master
****
Posts: 1436


WWW
« Reply #14 on: December 12, 2020, 04:07:54 PM »

MMA Handling quoted strings: Yes, it should have been done that way. But, that was then ... and I was still thinking of MMA as a slightly fancy metronome Smiley

I agree on the subs. We'll just leave it alone for now.

Best,
Logged

My online life: http://www.mellowood.ca
Pages: [1] 2
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.058 seconds with 19 queries.