PDA

View Full Version : last


maxxon
1st February 2012, 05:47
Hi all, I noticed that there is a special 'last' variable that appears to hold on to the last clip that was processed. I'm actually guessing. I've just seen some random references here and there about it and didn't think too much of it at the time. I has originally thought that it was some sort of copy of the last operation, but this doesn't seem to be the case.

Is there some special variable that states what the last operation completed as?

Thanks,


|\/|x

IanB
1st February 2012, 06:07
The script parser intrinsically interprets the following script segmentAviSource("Blah.avi")
FooBarFilter(1, 2, 3)
# End of scriptAs the following virtual scriptLast=AviSource("Blah.avi")
Last=Last.FooBarFilter(1, 2, 3)
# End of script
Return LastIt's just the way the language works.

TheFluff
1st February 2012, 10:58
If a statement does not explicitly assign its return value to a specific variable, the return value of said statement will automatically end up in last. Functions (and scripts) return last unless an explicit return statement says otherwise.

Gavino
1st February 2012, 11:28
From http://avisynth.org/mediawiki/Grammar:
All basic AviSynth scripting statements have one of these forms:

1. variable_name = expression
2. expression
3. return expression

In the first case, expression is evaluated and the result is assigned to variable_name. In the second case, expression is evaluated and the result, if a clip, is assigned to the special variable last. In the third case, expression is evaluated and is used as the "return value" of the active script block--that is either a function or the entire script. In the latter case, the return value is typically the video clip that will be seen by the application which opens the AVS file. As a shorthand, a bare expression as the final statement in a script (or script block) is treated as if the keyword return was present.
...
If a function expects a video clip as its first argument, and that argument is not supplied, then the clip in the special variable last will be used.
Some subtle points to note:
- last is only set if the statement is an expression whose value is a clip (that's what allows you to put things like LoadPlugin(...) without disturbing last);
- strictly speaking, last is not set on the final statement in a script (or function) - the value (if an expression) is simply returned;
- it's wrong to say the default return value (of a script or function) is last - if the final statement is a non-clip expression, its value will be returned even if last has a value (and if it's an assignment, a void ('undefined') value is returned).

Didée
1st February 2012, 11:36
BTW, I remember that one user found it "convenient" to use a custom variable "last" in self-designed functions, like

function foo(clip last)
{ last.somefilter() }

:eek:

How exactly is this handled? Could you possibly end up with a construct like result=last.last(last) ? (inherent last, function last, argument last)

Gavino
1st February 2012, 11:42
Inside a function, 'last' is just another local variable (each function has its own instance).
Using 'last' as an argument name just gives that 'last' an initial value.
So some people write
function foo(clip last)
{ somefilter() } # implicit last set from argument
which works, but I don't recommend as it's confusing.

Function names can co-exist with variables, so it's legitimate (if confusing) to also have a function called 'last'.

Didée
1st February 2012, 12:04
Well, I never actually tried that. Though, my expection is that the initial content of last (the object feeded in the function call) will survive as long as "inherent last" is not used within the function? But gets overwritten when something gets assigned to "inherent last"?

last = someclip.MakeRed() # make a red clip
last(last=last) # stomach wants to turn inside out
return(last)

function last(clip "last") {
MakeGreen() # object is "last", which is a red clip
return(last) # now "last" is a green clip. The original function input ("last") is not available anymore.
}

Gavino
1st February 2012, 12:15
Though, my expection is that the initial content of last (the object feeded in the function call) will survive as long as "inherent last" is not used within the function? But gets overwritten when something gets assigned to "inherent last"?
Yes, since they are the same variable - the 'inherent last' is (the same thing as) the function argument.

maxxon
1st February 2012, 14:18
Interesting, but is there a way of getting the last evaluated expression?


|\/|x

Gavino
1st February 2012, 14:29
is there a way of getting the last evaluated expression?
Do you mean the expression itself, or the value of the expression?
But in either case, I think the answer is 'no'.

maxxon
1st February 2012, 15:56
Do you mean the expression itself, or the value of the expression?
But in either case, I think the answer is 'no'.
The latter. :)


|\/|x

Gavino
2nd February 2012, 12:07
Just to add a note on what I said above:
strictly speaking, last is not set on the final statement in a script (or function) - the value (if an expression) is simply returned
You might think this is a meaningless distinction, since if it's the 'final' statement, what difference does it make whether last is updated, and how could you tell anyway? However, it does make a difference on things like this:

main.avs:

...Source(...)
c = Import("a.avs")
Filter4()
a.avs:

Filter1()
Filter2()
Filter3()
During the Import (which does not start a new scope), last is set first to the result of Filter1(), then to the result of Filter2(), but at no point is it set to the result of Filter3(), which is just the return value for Import itself and then gets assigned to c.
Consequently, the value of last used as the (implicit) input to Filter4 will be taken from the result of Filter2(), and not Filter3() (nor the source filter either).

The point here is that the last line of a.avs is equivalent to
return Filter3()
and not
last = Filter3()

Note that without the assignment to c, Filter3's result would be used by Filter4(), since then the Import line would be equivalent to
last = Import("a.avs")
causing 'last' to be updated.

gyth
2nd February 2012, 15:30
Function names can co-exist with variables, so it's legitimate (if confusing) to also have a function called 'last'.
This case wasn't clear to me, so I tested it.
blankclip
last #which last will this refer to?
info

function last(clip c){
c.pointresize(300,300)
}
This uses the clip, not the function with an implicit last.
blankclip.last calls the function.

Gavino
2nd February 2012, 17:32
blankclip
last #which last will this refer to?
info

function last(clip c){
c.pointresize(300,300)
}
This uses the clip, not the function with an implicit last.
That's right.
For a name with no following brackets, Avisynth first looks for a variable with that name. Only if none is found does it go on to see whether it can be interpreted as a function call. So in your example, last is interpreted as the variable.

blankclip.last calls the function.
Yes, because the dot notation only has one interpretation, as a function call, equivalent here to last(blankclip).

On a related topic, note that on Avisynth 2.58, blankclip without brackets will be taken as blankclip(last) if last has a defined clip value, instead of being taken as blankclip(). This is a bug which is fixed in 2.60 alpha 3.

maxxon
3rd February 2012, 16:58
This is interesting. How is resolving done and is it true for all cases? From what I gather from what was said in this thread and some testing...


if a keyword exists and expected, it is a keyword
if followed by parenthisis, and a function exists, it is a function, do signature resolution
if followed by parenthisis, and a builtin function exists, it is a builtin function, do signature resolution
if followed by parenthisis, and a filter function exists, it is a filter function, do signature resolution
if not followed by a parenthisis and

if variable exists (global or local), then it is a variable.
if builtin variable exists, then it is a builtin variable.
if function exists, then it is a function.



Something that I find a bit odd is that keywords are not ensured not to be used:

function function { } # succeeds to declare a function named function
function() # fails as it expects it to be a function declaration

global global = 3
assert(global == 3) # passes


true = 3
assert(true == 3) # passes


A function can overwrite a preexisting function or builtin function, doing this causes the previous one to be lost.

function import(string s)
{
assert(false)
}
import("somefile.avs") # assert is thrown



function x
{
}

function x
{
assert(false)
}

x # assert is thrown

There also seems to be a off by one error here:

function x(a) {}
x # no error, a in function x is undefined
x(1) # correct and no error
x(1,2) # error is correct


I'm guessing that functions/special function/filter functions are all in the same relolution scope where any can override another?

Am I understanding this correctly?


|\/|x

Gavino
3rd February 2012, 23:41
Function precedence rules are defined in Plugin autoload and name precedence (http://avisynth.org/mediawiki/Plugins#Plugin_autoload_and_name_precedence_v2):
The order in which function names take precedence is as follows:
user-defined function (always have the highest priority)
plugin-function (have higher priority than built-in functions, they will override a built-in function)
built-in function
Inside those groups the function loaded last takes precedence, there is no error in a namespace conflict.

Note that Avisynth supports function overloading, so supplied argument types are also used in resolving function calls.

There are no 'reserved' identifiers as such. Keywords are recognised solely by context and those words can also be used as identifiers in other contexts. Where ambiguity arises, the 'keyword' interpretation prevails (as in your call to 'function', but x = function() would work).

There also seems to be a off by one error here:

function x(a) {}
x # no error, a in function x is undefined
That's correct behaviour - no error, since taken as x(last).
Even if last has not been assigned (hence 'undefined'), it is acceptable to the function since a is untyped.

maxxon
5th February 2012, 15:41
Note that Avisynth supports function overloading, so supplied argument types are also used in resolving function calls.
Yeah, that's what I referred to signature resolution.

There are no 'reserved' identifiers as such. Keywords are recognised solely by context and those words can also be used as identifiers in other contexts. Where ambiguity arises, the 'keyword' interpretation prevails (as in your call to 'function', but x = function() would work).
Interesting, can result in some confusing code, but interesting.


That's correct behaviour - no error, since taken as x(last).
Even if last has not been assigned (hence 'undefined'), it is acceptable to the function since a is untyped.

But this only works if it is untyped? Why is that? Why doesn't this work if say a was a clip as last can only be a clip or undefined anyway.


|\/|x

Gavino
5th February 2012, 16:36
But this only works if it is untyped? Why is that? Why doesn't this work if say a was a clip as last can only be a clip or undefined anyway.
function x1(a)
a can be any type, including void (aka 'undefined').

function x2(val a)
Same as above.

function x3(clip "a")
a is optional, so can be a clip or 'undefined'.

function x4(clip a)
a is mandatory and must be a clip ('undefined' not accepted).

Results of calling these without an argument:
x1 or x1() : OK, uses last as argument, even if 'undefined'.
x2 or x2() : same
x4 or x4() : error if last is 'undefined'.

The interesting case is x3.
x3() ignores last, since the argument is optional, and in v2.60 x3 does the same.
However, in v2.58 and earlier, x3 will be taken as x3(last) - this is a bug.

maxxon
6th February 2012, 16:00
That's interesting. I'll have to try a few things to sus out behaviour and document for my library. It may do some things that may be unexpected with the array declaration and others that rely on the parameters's library.


|\/|x