Nested function: Difference between revisions
Content added Content deleted
m (→{{header|AppleScript}}: Tidied.) |
(→{{header|Phix}}: replaced with two work-arounds) |
||
Line 1,017: | Line 1,017: | ||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
There is only partial support for nested functions in Phix. Some prior work (over a single afternoon) has been left |
|||
{{improve|Phix|The Phix compiler needs enhancing to fully support nested functions.}} |
|||
unfinished, anyone interested can see it at [[Nested_function/Phix]], but it was just enough to open the door for |
|||
the two following reasonably acceptable work-arounds.<br> |
|||
Prior to this task, Phix had no support whatsoever for nested functions.<br> |
|||
Note that in both the following you cannot reference any local variables or parameters of the containing function, |
|||
Instead I have taken the first baby steps and documented them.<br> |
|||
but must pass in everything you need explicitly, and anything you need to update must be a reference type, which |
|||
If your distribution does not include the following demo, you need a later version (not yet uploaded at the |
|||
is only dictionaries and class instances, not integers, atoms, sequences, strings, or any user-defined types, as |
|||
time of writing). The source of that demo contains far more detailed information, and any updates.<br> |
|||
they are all effectively read-only. Also note that the compiler will not (as yet) issue any proper errors or |
|||
warnings should you break that restriction, instead it simply won't work as hoped for. |
|||
Yes, this is pig-ugly, and incomplete. But it is a good start for anyone that needs nested functions, and |
|||
=== using a dictionary === |
|||
shows what can be done in just a few (less than six) hours. |
|||
<lang Phix>function MakeList(string sep=". ") |
|||
function MakeItem(integer env, string sep) |
|||
NB as it stands, the compiler front-end "thinks" that l_counter and sep live in the same place. They are |
|||
integer counter = getd("counter",env)+1 |
|||
properly separate in the runtime/VM, but the front-end will happily emit nonsense code if you let it. |
|||
setd("counter",counter,env) |
|||
<lang Phix>-- |
|||
return sprintf("%d%s%s",{counter,sep,{"first","second","third"}[counter]}) |
|||
-- demo\rosetta\Nested_function.exw |
|||
-- ================================ |
|||
-- |
|||
#ilASM{ jmp :fin |
|||
-- |
|||
-- This is, of course, something the compiler should end up doing automatically, |
|||
-- and this assembly, or something similar, should be hidden away in builtins/VM. |
|||
-- |
|||
:%opGetnlv -- [edi] := [esi] from frame edx |
|||
-- nb no reference counting (would be rqd) |
|||
[32] |
|||
sub esi,ebp -- --> frame offset |
|||
@@: |
|||
mov ecx,[ebp+20] -- ebp_prev |
|||
cmp [ecx+8],edx -- rtn |
|||
jne @b |
|||
mov eax,[esi+ecx] |
|||
mov [edi],eax |
|||
[64] |
|||
sub rsi,rbp -- --> frame offset |
|||
@@: |
|||
mov rcx,[rbp+40] -- rbp_prev |
|||
cmp [rcx+16],rdx -- rtn |
|||
jne @b |
|||
mov rax,[rsi+rcx] |
|||
mov [rdi],rax |
|||
[] |
|||
ret |
|||
:%opSetnlv -- [edi] in frame edx := [esi] (zeroed) |
|||
[32] |
|||
sub edi,ebp -- --> frame offset |
|||
@@: |
|||
mov ecx,[ebp+20] -- ebp_prev |
|||
cmp [ecx+8],edx -- rtn |
|||
jne @b |
|||
mov eax,[esi] |
|||
mov [edi+ecx],eax |
|||
-- mov [esi],ebx -- zero src |
|||
[64] |
|||
sub rdi,rbp -- --> frame offset |
|||
@@: |
|||
mov rcx,[rbp+40] -- rbp_prev |
|||
cmp [rcx+16],rdx -- rtn |
|||
jne @b |
|||
mov eax,[rsi] |
|||
mov [rdi+rcx],rax |
|||
-- mov [rsi],rbx -- zero src |
|||
[] |
|||
ret |
|||
::fin |
|||
} |
|||
function MakeList(string sep=". ") |
|||
integer counter = 0 |
|||
function MakeItem() |
|||
-- -- what we'd really like to see: |
|||
-- counter += 1 |
|||
-- return sprintf("%d%s%s",{counter,sep,{"first","second","third"}[counter]}) |
|||
-- bar these locals, some idea of what the compiler should be doing: |
|||
integer l_counter |
|||
string l_sep |
|||
#ilASM{ |
|||
[32] |
|||
mov edx,routine_id(MakeList) |
|||
lea esi,[counter] |
|||
lea edi,[l_counter] |
|||
call :%opGetnlv |
|||
lea esi,[sep] |
|||
lea edi,[l_sep] |
|||
call :%opGetnlv |
|||
[64] |
|||
mov rdx,routine_id(MakeList) |
|||
lea rsi,[counter] |
|||
lea rdi,[l_counter] |
|||
call :%opGetnlv |
|||
lea rsi,[sep] |
|||
lea rdi,[l_sep] |
|||
call :%opGetnlv |
|||
[] |
|||
} |
|||
l_counter += 1 |
|||
#ilASM{ |
|||
[32] |
|||
mov edx,routine_id(MakeList) |
|||
lea esi,[l_counter] |
|||
lea edi,[counter] |
|||
call :%opSetnlv |
|||
[64] |
|||
mov rdx,routine_id(MakeList) |
|||
lea rsi,[l_counter] |
|||
lea rdi,[counter] |
|||
call :%opSetnlv |
|||
[] |
|||
} |
|||
string res = sprintf("%d%s%s",{l_counter,l_sep,{"first","second","third"}[l_counter]}) |
|||
#ilASM{ |
|||
[32] |
|||
mov [l_sep],ebx -- (in lieu of proper refcounting) |
|||
[64] |
|||
mov [l_sep],rbx -- (in lieu of proper refcounting) |
|||
[] |
|||
} |
|||
return res |
|||
end function |
end function |
||
integer counter = new_dict({{"counter",0}}) |
|||
sequence res = {} |
sequence res = {} |
||
for i=1 to 3 do |
for i=1 to 3 do |
||
res = append(res,MakeItem()) |
res = append(res,MakeItem(counter,sep)) |
||
end for |
end for |
||
return res |
return res |
||
end function |
end function |
||
?MakeList()</lang> |
?MakeList()</lang> |
||
{{out}} |
{{out}} |
||
Line 1,147: | Line 1,045: | ||
{"1. first","2. second","3. third"} |
{"1. first","2. second","3. third"} |
||
</pre> |
</pre> |
||
=== using a class === |
|||
Same output. I trust it is obvious that if you passed in c.count, you would not be able to update it. |
|||
<lang Phix>class counter |
|||
public integer count |
|||
end class |
|||
function MakeList(string sep=". ") |
|||
function MakeItem(counter c, string sep) |
|||
c.count += 1 |
|||
return sprintf("%d%s%s",{c.count,sep,{"first","second","third"}[c.count]}) |
|||
end function |
|||
counter c = new() |
|||
sequence res = {} |
|||
for i=1 to 3 do |
|||
res = append(res,MakeItem(c,sep)) |
|||
end for |
|||
return res |
|||
end function |
|||
?MakeList()</lang> |
|||
=={{header|PHP}}== |
=={{header|PHP}}== |