CoffeeScript variable scope - coffeescript

Is there any way to declare a variable at "file" scope (which will be closured by CS), without initializing it? A contrived example:
init = ->
counter = 0
inc = ->
counter += 1
This won't work, because you need to declare "counter". Adding "counter = 0" to the top would make it work, but the "= 0" is unnecessary. (A more realistic example would involve something that accesses the DOM on page load - there's no way to correctly initialize it in "file" scope.)

You'll have to define it on the outer scope, as you mentioned.
counter = null
init = ->
counter = 0
inc = ->
counter += 1

If your functions where part of an object you could use #counter, like this:
obj =
init: ->
#counter = 0
inc: ->
#counter += 1

You can say `var counter;` with backticks and that is passed literally through to the generated javascript.
When you have a problem like this, look at the generated javascript. It will be extremely clear that the variable scope is lexically limited to the function.
Looking at the generated javascript is often a good way to understand what the behavior of coffeescript constructs is.

Related

Strange behavior during variables assignment in Scala

How can I define variables inside if-block ?
if ( 2 > 1 ){
val t = 6
}
print(t)
This simple code returns error : not found: value t
Your code's t variable is only defined within if block. So you can use it only inside this block. One reason for this behavior is a question: Which value has t when the condition of the if statement is false.
If you want to use the t variable outside this scope you can do the following:
Put if block in the assigning like this:
val t = if (2 > 1) 0 else 1
Use var keyword to make the t mutable and define it before if block with the default value:
var t = 1
if (2 > 1) {
t = 0
}
In any case, you need a value for else case to be able to define the t variable.

Call a script with definitions in a function

We have a script that defines values to names similar to #define in c. For example:
script.m:
ERR_NOERROR = 0;
ERR_FATAL = 1;
This script already exists and is used for value replacement when reading data from files.
Now we have a function (or more) that does some analysis and we would like to use the same definition in this function to avoid magic numbers. But when the script is called from the function we get an error.
Attempt to add "ERR_NOERROR" to a static workspace.
See MATLAB Programming, Restrictions on Assigning to Variables for details.
And this does not help much in the understanding of the problem.
The question is how can we make these definitions visible/usable in the functions with having to copying it every time.
Example:
function foo = bar(a)
run(script.m) %also tried running it without the run command
if a == ERR_NOERROR
foo = 5;
else
foo = 6;
end
end
edit:
There was a nested function,below in the function which I was not aware of. This explains the problem.
This kind of scoping error happens when you use nested or anonymous function within a function. The solution is well documented.
To your case, you can avoid nested function, or "Convert the script to a function and pass the variable using arguments", as the documentation suggests.
EDIT: I should have made it clear that the error occurs even if the script is not called within the nested function. Similar scenario is that, in debug mode (by setting up a break point), it will be an error if one tries to create a temporal variable to test something.
This is not a direct answer, rather a recommendation to switch to another method, which will not be mixing scope and workspace.
Instead of defining your constant in a script, you could make a class containing only constant properties. ex: code for error_codes.m:
classdef error_codes
% ---------------------------------------------------------------------
% Constant error code definition
% ---------------------------------------------------------------------
properties (Constant = true)
noerror = 0 ;
fatal = 1 ;
errorlvl2 = 2 ;
errorlvl3 = 3 ;
warning = -1 ;
% etc ...
end
end
I use this style for many different type of constants. For tidiness, I groups them all in a Matlab package directory (The directories which starts with a + character.
The added benefit of using constant class properties is the safety that the values cannot be changed in the middle of the code (your variables defined in a script could easily be overwritten by a careless user).
So assuming my file error_codes.m is placed in a folder:
\...somepath...\+Constants\error_codes.m
and of course the folder +Constants is on the MATLAB path, then to use it as in your example, instead of calling the script, just initialise an instance of the class, then use the constant values when you need them:
function foo = bar(a)
ERR = Constants.error_codes ;
if a == ERR.noerror
foo = 5;
else
foo = 6;
end
or it can works in switch statement too:
switch a
case ERR.noerror
foo = 5 ;
case ERR.warning
foo = 42 ;
case ERR.fatal
foo = [] ;
end

TestComplete/JScript- Using a variable in an object path

I am using TestComplete with JScript testing a webpage that has elements that I declare as a variable to make it easier to test the element later. They all have a path like:
var check1 = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0).Label(0).Checkbox(0)
The elements are dynamic, so there is no telling how many there are when the test is run. I was hoping there was some way to loop through and declare the elements, but it would involve declaring the element like this:
var check1 = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0).Label(x).Checkbox(0)
where x is the counter variable. The problem is that TestComplete sees this as a literal path and does not recognize x as a variable.
Is there any way to do this with TestComplete using JScript? Or convert a string to an object? I think I can work with that, too.
My guess is that since you store the reference in variable check1, the variable x is updated but the x in variable check1 still holds it's original value (1).
Workaround
Keep the first part of the path static in the variable, then update x and assign it to the label.
var path = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0);
// path to the Checkbox
path.Label(x).Checkbox(0);
// or if you want to loop over it
for (var x = 0, len = 8; i < len; x += 1) {
if (path.Label(x).Checkbox(0).value === 'something') {
console.log('hooray!');
}
}

1-line try/catch equivalent in MATLAB

I have a situation in MATLAB where I want to try to assign a struct field into a new variable, like this:
swimming = fish.carp;
BUT the field carp may or may not be defined. Is there a way to specify a default value in case carp is not a valid field? For example, in Perl I would write
my $swimming = $fish{carp} or my $swimming = 0;
where 0 is the default value and or specifies the action to be performed if the assignment fails. Seems like something similar should exist in MATLAB, but I can't seem to find any documentation of it. For the sake of code readability I'd rather not use an if statement or a try/catch block, if I can help it.
You can make your own function to handle this and keep the code rather clear. Something like:
swimming = get_struct(fish, 'carp', 0);
with
function v = get_struct(s, f, d)
if isfield(s, f)
v = s.(f); % Struct value
else
v = d; % Default value
end
Best,
From what I know, you can't do it in one line in MATLAB. MATLAB logical constructs require explicit if/else statements and can't do it in one line... like in Perl or Python.
What you can do is check to see if the fish structure contains the carp field. If it isn't, then you can set the default value to be 0.
Use isfield to help you do that. Therefore:
if isfield(fish, 'carp')
swimming = fish.carp;
else
swimming = 0;
end
Also, as what Ratbert said, you can put it into one line with commas... but again, you still need that if/else construct:
if isfield(fish,'carp'), swimming = fish.carp; else, swimming = 0;
Another possible workaround is to declare a custom function yourself that takes in a structure and a field, and allow it to return the value at the field, or 0.
function [out] = get_field(S, field)
if isfield(S, field)
out = S.(field);
else
out = 0;
end
Then, you can do this:
swimming = get_field(fish, 'carp');
swimming will either by 0, or fish.carp. This way, it doesn't sacrifice code readability, but you'll need to create a custom function to do what you want.
If you don't like to define a custom function in a separate function file - which is certainly a good option - you can define two anonymous functions at the beginning of your script instead.
helper = {#(s,f) 0, #(s,f) s.(f)}
getfieldOrDefault = #(s,f) helper{ isfield(s,f) + 1 }(s,f)
With the definition
fish.carp = 42
and the function calls
a = getfieldOrDefault(fish,'carp')
b = getfieldOrDefault(fish,'codfish')
you get for the first one
a = 42
and the previous defined default value for the second case
b = 0

loop starting from given index

Considering the following javascript, what's the best way to write this loop in coffee script, given the initial index is greater than 0:
function mixin(target, source, methods) {
for (var i = 2, l = arguments.length; i < l; i++){
var method = arguments[i];
target[method] = source[method].bind(source)
}
}
Automatic code converters suggest use a while loop like this:
mixin = (target, source, methods) ->
i = 2
l = arguments.length
while i < l
method = arguments[i]
target[method] = source[method].bind(source)
i++
Is there a cleaner way to do this?
You'd usually use a splat in CoffeeScript when defining your mixing function:
The JavaScript arguments object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats ..., both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable.
So you'd say:
mixin = (target, source, methods...) ->
# splat ----------------------^^^
for method in methods
target[method] = source[method].bind(source)
and your problem goes away. The splat in the argument list will collect all the arguments after source into a methods array for you so you won't have to worry about arguments at all; the splat also makes the function's signature nice and obvious.
Use an exclusive range (triple dot, excludes the number at the highest.
for i in [2...arguments.length]
method = arguments[i]
target[method] = source[method].bind(source)
If you have 5 things in your args, this will hit indexes 2, 3 and 4.