I find that I am often wanting to append values to dictionary lists, but only if the value is not in the list already. Therefore, I'm trying to separate this out into a procedure, and I can't figure out why the following code doesn't achieve this:
proc DictAdd {_dictionary key value} {
upvar 1 $_dictionary dictionary
if { $value ni [dict get $dictionary $key] } {
dict lappend dictionary $key $value
}
}
Calling this procedure returns the following error:
can't read "dictionary": no such variable
while executing
"dict get $dictionary $key"
(procedure "DictAdd" line 5)
invoked from within
"DictAdd $files baseline $fileName"
(procedure "getFilesToLoad" line 53)
invoked from within
...
Could someone please tell me what I'm doing wrong here? Thanks.
In the invocation
DictAdd $files baseline $fileName
$files is the value of your dictionary, but DictAdd expects the name of the dictionary variable. If you instead invoke it like this:
DictAdd files baseline $fileName
the command works as designed.
BTW: if you define DictAdd like this:
proc DictAdd {_dictionary key value} {
upvar 1 $_dictionary dictionary
if {[dict exists $dictionary $key]} {
dict with dictionary {
upvar 0 $key values
if {$value ni $values} {
lappend values $value
}
}
} else {
dict lappend dictionary $key $value
}
}
you don't get an error message if the key is missing (it adds the value under that key) (the dictionary still needs to exist outside DictAdd) and the checking/adding code gets a little less messy.
Why the name? It’s because of how upvar works. The command takes a stack level (in this case 1 = the caller’s level) and a variable name (contained in _dictionary; “files” in this case); using those it locates a variable outside the executing command, and creates a local alias inside the executing command (in this case named dictionary: files outside is now basically the same variable as dictionary inside). If you pass something else, e.g. the value of files, say {baseline {a b c}}, upvar will look for a variable called {baseline {a b c}} and most likely not find it. It will create the alias anyway, and if you initialize it, a variable called {baseline {a b c}} at the caller’s level will actually be created. But, again, you will want to use the name of the variable (of course, the name of the variable could be the value of another variable when you invoke the command…).
Documentation: dict, if, lappend, ni operator, proc, upvar
The problem is most likely that the dictionary variable that you are referring to is actually unset, and so impossible to read. Try this:
proc DictAdd {_dictionary key value} {
upvar 1 $_dictionary dictionary
if {![info exists dictionary]} {
set dictionary [dict create $key [list $value]]
} elseif {$value ni [dict get $dictionary $key]} {
dict lappend dictionary $key $value
}
}
Related
I am writing a custom User defined function in kusto where I want to have some optional parameters to the function which will be used in the "where" clause. I want to dynamically handle this. For example: If the value is present, then my where clause should consider that column filter, else it should not be included in the "where" clause>
Eg (psuedo code where value is not null):
function Calculate(string:test)
{
T | where test == test | order by timestamp
}
Eg (psuedo code where value is null or empty. My final query should look like this):
function Calculate(string:test)
{
T | order by timestamp
}
What is the efficient way to implement this. I will call this function from my c# class.
you can define a function with a default value for its argument, and use the logical or operator and the isempty() function to implement the condition you've described.
for example:
(note: the following is demonstrated using a let statement, but can be applied similarly to stored functions)
let T = range x from 1 to 5 step 1 | project x = tostring(x)
;
let F = (_x: string = "") {
T
| where isempty(_x) or _x == x
| order by x desc
}
;
F("abc")
if you run F() or F("") (i.e. no argument, or an empty string as the argument) - it will return all values 1-5.
if you run F("3") - it will return a single record with the value 3.
if you run F("abc") - it will return no records.
I would like to write a proc which can accept dict as an optional argument or create new if none was given. My attempt, however, fails with
too many fields in argument specifier "dictionary [dict create]"
proc getOpts { {dictionary [dict create]} } {
upvar $dictionary dct
for { set i 0 } { $i < $::argc } { incr i } {
set key [lindex $::argv $i]
if { [string index $key 0] == "-" } {
incr i
dict set dct [string trimleft $key "-"] [lindex $::argv $i]
} else {
dict set dct $key ""
}
}
}
As dictionaries are pure values, I thought this could work. It looks I might have been wrong. Still, I would like to know why it doesn’t work.
The reason it does not work is that the argument variable list is a true list, not a command evaluation context. That means the [dict create] is getting parsed as two separate words, and proc doesn't like that. Fortunately, a dict create with no other arguments is just another way of saying the empty string: use {} or "" instead.
The bigger problem is that you are using that with upvar; you probably don't want to do that. The right approach is likely to be to not do the variable binding, and instead to just return the dictionary from the procedure: the caller can save it in one of its own variables if it cares to.
I have a php script which was written on php 5.6.19, works on 5.3 version to, with some installed addons.
I decide to try execute it on php7.
The special of the script that I am initializing a class with parameter by reference via creating a new instance with Reflection::class. And there warning then waited variable by reference but value received.
Definition of the class' constructor method tried to create an instance from:
public function __construct($user, IDatabase &$repository, $errors = null);
Sample of code where this constructor is used:
// define manager type to create (all managers has the same constructor)
$manager = $managersNamespace . ucfirst($this->_manager) . "Manager";
// trying to create the manager
// !!!And here a Warning occurs
$reflect = new \ReflectionClass($manager);
$manager = $reflect->newInstance($user, $database, $errors);
After these I am invoking a method I need, and here the fatal error with stopped the script:
$method = "show" . ucfirst($this->_page) . "Page";
$reflect->getMethod($method)->invoke($manager);
I didn't see any changes in documentation. Anyone had the same issue?
First and foremost, why are you passing an object by reference !?
Objects have pass-by-reference semantics, forcibly trying to pass objects by reference has not made good sense since PHP 4.
Just remove the & ...
Let's ignore that, and pretend there is still a problem, so that you can try to understand what is going on.
To break down the problem, first you need to understand the distinction between a variable and an expression:
mine(1 + 2);
The argument to mine has no name, it's represented by a temporary variable in the engine: it's an expression.
mine(1);
The argument to mine has no name, it's not an expression, but a literal constant, represented by a compiler variable in the engine. It's similar to a temporary variable, a kind of constant expression.
mine($a);
The argument to mine has a name, which you can use to refer to it's value. It's a normal variable.
Only variables can be passed by reference because you cannot refer to expressions or literal constants
Next you need to understand why we pass-by-reference:
function mine(int $thing) {
$thing++;
}
$a = 1;
mine($a);
var_dump($a); // int(1)
In this code, $a is passed to mine() by value, so that the changes that mine() make to $thing are only visible inside the scope of mine. $a is unchanged after the call to mine() returns because $a and $thing are distinct, having been passed-by-value, which means it's value was copied on to the call stack for the invocation of mine().
function mine(int &$thing) {
$thing++;
}
$a = 1;
mine($a);
var_dump($a); // int(2)
In the code above, $a is passed to mine() by reference, this means that $a and $thing are no longer distinct. The changes mine() make to $thing are now visible after the call to mine() returns.
The last piece in the puzzle is Reflection:
function mine(int &$thing) {
$thing++;
}
$a = 1;
$reflector = new ReflectionFunction("mine");
$reflector->invoke($a);
The code above will raise:
Warning: Parameter 1 to mine() expected to be a reference, value given in /usr/src/php-src/refs.php on line 9
This is because ReflectionFunction::invoke and similar reflection functions (ReflectionClass::newInstance) accept their parameters by value and pass them onto the invoked function by value.
But ...
There is still a difference between pass-by-reference semantics, and passing by reference, a dangerous one:
class Foo {
public function qux() {}
}
class Bar {}
function mine(Foo &$foo) {
$foo = new Bar();
}
$foo = new Foo;
mine($foo);
$foo->qux();
Will obviously yield:
PHP Fatal error: Uncaught Error: Call to undefined method Bar::qux() in /usr/src/php-src/refs.php:16
Stack trace:
#0 {main}
thrown in /usr/src/php-src/refs.php on line 16
The declaration of mine() tells lies about the type safety of it's parameter. Type safety is only guaranteed upon entry to the function, the function body is free to break type safety, but it doesn't usually affect the caller when relying on the engines pass by reference semantics for objects.
This is an extremely scary kind of API, that should be avoided.
I build a dictionary in tcl 8.4 inside a procedure. How do I use the constructed dictionary in another procedure. I have added a sample code of how I construct a dictionary in tcl 8.4. I know tcl 8.5 has in built in 'dict' option, but I have to use tcl 8.4.
proc a {} {
set test(key1) "value1"
set test(key2) "value2"
lappend keylist "key1"
lappend keylist "key2"
foreach key $keylist {
puts "value of $key is $test($key)"
}
}
So the procedure mentioned above builds a dictionary. But since tcl 8.4 interpreter interprets every line "$test($key)" as a separate variable how can I make it global so that I can use it in another procedure.
You can use the global command to make a variable or array a global one.
For instance:
proc a {} {
global test
set test(key1) "value1"
set test(key2) "value2"
lappend keylist "key1"
lappend keylist "key2"
foreach key $keylist {
puts "value of $key is $test($key)"
}
}
a
# Outputs
# value of key1 is value1
# value of key2 is value2
# Now use them in another proc...
prob b {} {
global test
puts $test(key1)
puts $test(key2)
}
b
# Outputs
# value1
# value2
If you are able to return the dictionary, you could pass it in to another proc as a parameter. (Would have preferred to put this in the comment section, but I do not have the required rep)
I want to see if a variable exists - i.e. that I have created in.
if(exists(this.mydict))
{ //append my dict
}else
{
// initialize dict
}
Trouble is this fails on
Error in exists(this.mydict)
What am I doing wrong?
How can I extend the exists function to work with the following:
Any ideas how I would extend to this to looking at seeing whether a nested dictionary would also exist. I.e. for example: if(exists("mylists[[index]]['TSI']")), where the mylists object is a dictionary look up that also wants to contain a nested dictionary.
exists() function takes a character argument with the variable name:
if(exists("this.mydict")){
# you can use this.mydict here
}else{
# initialize this.mydict
# e.g. this.mydict <- "some value here"
}