Re: scope of variable in NCL

From: Jonathan Vigh <vigh_at_nyahnyahspammersnyahnyah>
Date: Sun Oct 18 2009 - 15:31:18 MDT

Saji,
    Out of curiosity, I tried writing the wrapper I described yesterday.
The code is attached. Basically, you give it the name (as a string) of a
function which has been previously defined in the program (or from an
external library), and you give it the list of arguments (also strings)
as they would appear when you the call the function (so this should
handle multidimensional variables, subsets, etc.). Then you call the
function 'check_whether_function_arguments_are_protected' instead of
calling the function itself. If the arguments change during the function
call, a message is printed stating which variable changed.

This might be handy for people who might want to check their libraries
for "rogue" functions, or if their functions have a lot of arguments or
are complicated.

Unfortunately, I couldn't "wrap" this to actually return what the
function would have returned - I think NCL would need an "eval"
statement to provide that (or this function would have to be made
internal to NCL). Anyway, I can see your point about this behavior and
how it could cause problems. But as long as we know what the "rules"
are, I suppose we can work with them. I can think of ways to exploit the
way that things currently work, so I'm not sure it would be good to
completely prevent this functionality. I like how Fortran allows you to
specify an "in" or "inout" parameter for the arguments of functions and
procedures. This might be the best solution. It seems that right now,
the default is "inout" IF your arguments have the same name inside your
function as in your program.

Jonathan

Jonathan Vigh wrote:
> Hi Saji,
> If you're paranoid/worried about this issue, a simple workaround
> is to simply assign the values to different variables once they've
> been read into a function. Since you are only concerned about the
> function output, this will *guarantee* that variables of the same name
> as arguments passed to the function won't get accidentally affected.
>
> Example:
>
> function safe(arg1,arg2,arg3)
> begin
>
> local_arg1 = arg1
> local_arg2 = arg2
> local_arg3 = arg3
>
> . . . do some stuff with local_args . . .
>
> return(stuff)
> end
>
> If you're writing libraries, it might be worth the extra little
> trouble - and this wouldn't require a redesign of NCL :) If you're
> worried about this issue with other people's libraries, you could
> probably write a wrapper function to check the argument values, call
> the function, then check the arguments again. Not ideal, but it might
> not be that hard.
>
> Have a good weekend,
> Jonathan
>
>
>
> Saji wrote:
>> Hi Dave and Wei and others,
>>
>> Thanks for the feedback and clarification. I understand it
>> , but doesn't it illustrate a situation, where some unsuspecting
>> user accidentally uses the same name somewhere in his script
>> as the function argument and lands up a nasty surprise. If the
>> library developer cannot ensure the 'local' scope of the arguments
>> within the function, it is a bit inconvenient, as the developer
>> has to be careful not to change the arguments that are passed on
>> to the function. I still wonder why this behaviour is implemented
>> in NCL functions, as a function is supposed to return a single data object.
>> ...why should it change other variables or arguments that it does not
>> return. Also this means that there is no real difference between a
>> so called 'procedure' and 'function'. If I am a paranoid user, I would have
>> to check all function definitions, before I feel confident to use them.
>>
>> I wonder if it will be possible to rethink this design issue in NCL at
>> least for functions --
>>
>> Best regards,
>> saji
>>
>>
>> * David Brown <dbrown@ucar.edu> [2009-10-16 09:29:30 -0600]:
>>
>>
>>> Hi Saji,
>>> Wei is quite correct about this case. Arguments are passed by
>>> reference and the variables they reference can be modified inside
>>> the function. The 'local' statement has no meaning for arguments.
>>> You can ignore the last message I sent. I apologize that I did not
>>> notice that 'a' was being used as an argument to the function.
>>> -dave
>>>
>>> On Oct 16, 2009, at 8:09 AM, Wei Huang wrote:
>>>
>>>
>>>> Saji,
>>>>
>>>> That is because you use "a" as an argument element, and local.
>>>>
>>>> If you do "print("" + test(0,1,2))", then you can not change "a"
>>>> out side your function.
>>>>
>>>> Wei Huang
>>>> huangwei@ucar.edu
>>>> VETS/CISL
>>>> National Center for Atmospheric Research
>>>> P.O. Box 3000 (1850 Table Mesa Dr.)
>>>> Boulder, CO 80307-3000 USA
>>>> (303) 497-8924
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Oct 15, 2009, at 11:03 PM, Saji N Hameed wrote:
>>>>
>>>>
>>>>> Even declaring "a" as local does not seem to work :(
>>>>> ncl 1> function test(a,b,c)
>>>>> ncl 2> local a,b,c
>>>>> ncl 3> begin
>>>>> ncl 4> a=-999
>>>>> ncl 5> return(1)
>>>>> ncl 6> end
>>>>> ncl 7> print(""+test(a,1,2))
>>>>> (0) 1
>>>>> ncl 8> print(""+a)
>>>>> (0) -999
>>>>>
>>>>> * Saji N Hameed <saji@apcc21.net> [2009-10-16 13:59:15 +0900]:
>>>>>
>>>>>
>>>>>> Dear NCL Developers,
>>>>>>
>>>>>> I suppose that the following should not happen. Isn't it a bug?
>>>>>> We are told that variables inside a function have local scope, and
>>>>>> this unusual feature can lead to a lot of headaches. Any solutions,
>>>>>> other than "local a" or using another variable name than "a"
>>>>>>
>>>>>> Copyright (C) 1995-2009 - All Rights Reserved
>>>>>> University Corporation for Atmospheric Research
>>>>>> NCAR Command Language Version 5.1.1
>>>>>> The use of this software is governed by a License Agreement.
>>>>>> See http://www.ncl.ucar.edu/ for more details.
>>>>>> ncl 0> a=5
>>>>>> ncl 1> function testme(a,b,c)
>>>>>> ncl 2> begin
>>>>>> ncl 3> a=2
>>>>>> ncl 4> return(1)
>>>>>> ncl 5> end
>>>>>> ncl 6> print(""+a)
>>>>>> (0) 5
>>>>>> ncl 7> print(""+testme(a,1,2))
>>>>>> (0) 1
>>>>>> ncl 8> print(""+a)
>>>>>> (0) 2
>>>>>>
>>>>>> Thanks,
>>>>>> saji
>>>>>> --
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> ncl-talk mailing list
>>>>>> List instructions, subscriber options, unsubscribe:
>>>>>> http://mailman.ucar.edu/mailman/listinfo/ncl-talk
>>>>>>
>>>>>>
>>>>> -- Saji N. Hameed
>>>>>
>>>>> APEC Climate Center
>>>>> 1463 U-dong, Haeundae-gu, +82 51 745
>>>>> 3951
>>>>> BUSAN 612-020, KOREA saji@apcc21.net
>>>>> Fax: +82-51-745-3999
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> ncl-talk mailing list
>>>>> List instructions, subscriber options, unsubscribe:
>>>>> http://mailman.ucar.edu/mailman/listinfo/ncl-talk
>>>>>
>>>> _______________________________________________
>>>> ncl-talk mailing list
>>>> List instructions, subscriber options, unsubscribe:
>>>> http://mailman.ucar.edu/mailman/listinfo/ncl-talk
>>>>
>>>
>>
>>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> ncl-talk mailing list
> List instructions, subscriber options, unsubscribe:
> http://mailman.ucar.edu/mailman/listinfo/ncl-talk
>

load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/contributed.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/shea_util.ncl"
;*********************************************************
; set some global variables so that they are available to all the
; units of this program

  cr = inttochar(10)
  doublequote = inttochar(34)

;*********************************************************
; function protect_function_arguments
;
; This function returns the output of a given function
; name and argument list, making sure that the arguments
; are not affected by calculations inside of the function.
;*********************************************************
undef("check_whether_function_arguments_are_protected")
function check_whether_function_arguments_are_protected(funcname[1]:string,argnames[*]:string)

; local

begin

  nargs = dimsizes(argnames)

;=====================================================================================================
; Begin dynamic code generation section to read in the data from the file

     module_filename = "dynamically_generated_code_for_check_whether_function_arguments_are_protected.ncl"

     module_contents = ""

     do iarg = 0, nargs-1
        module_contents = module_contents + cr + " " + argnames(iarg) + "_value_before_call" + " = " + argnames(iarg)
     end do

     module_contents = module_contents + cr + cr + " function_output = " + funcname + "("
     
     do iarg = 0, nargs-2
        module_contents = module_contents + argnames(iarg) + ","
     end do
     
     module_contents = module_contents + argnames(nargs-1) + ")" + cr + cr

     do iarg = 0, nargs-1
        module_contents = module_contents + " if (any(" + argnames(iarg) + " .ne. " + argnames(iarg) + "_value_before_call)) then" + cr
        module_contents = module_contents + " print(" + doublequote + "Arguments are NOT protected: during the function call, the argument '" + argnames(iarg) + "' changed from " + doublequote + " + " + argnames(iarg) + \
                                                        "_value_before_call + " + doublequote + " to " + doublequote + " + " + argnames(iarg) + ")" + cr
        module_contents = module_contents + " end if" + cr + cr
     end do

; Now write the module contents out to a file
     if(isfilepresent(module_filename))
       system("/bin/rm -f " + module_filename) ; remove a pre-existing file
     end if
  
     asciiwrite(module_filename,module_contents)

; End dynamic code generation section
;=====================================================================================================

     return(module_filename)

end

;*********************************************************
; function foo
;
; An example function which could potentially modify
; arguments if they have the same name as variables
; within the function.
;*********************************************************
undef("foo")
function foo(a,b,c)
local a, b, c

begin
  output = a + b + c
  a = 3
  return(output)
end

;********************
; main program block
;********************
begin

  a = 1
  b = 2
  c = 3

  module_filename = check_whether_function_arguments_are_protected("foo",(/"a","b","c"/))
  loadscript(module_filename)

end

_______________________________________________
ncl-talk mailing list
List instructions, subscriber options, unsubscribe:
http://mailman.ucar.edu/mailman/listinfo/ncl-talk
Received on Sun Oct 18 15:31:37 2009

This archive was generated by hypermail 2.1.8 : Thu Oct 22 2009 - 13:09:30 MDT