tcl find the max element of a list - tcl

i am new to tcl , i am trying to get the max element in a given list
i wrote a function that prints the max but it is not working properly
here is the code
proc findmax { items } {
set max 1
foreach i $items {
if { $i > $max } {
set $max $i
}
}
puts "max is = $max"
}
and i called the function like this :
findmax $items
this is the list i passed :
set items { 12 2 5 4 2 6 7 55 8 9 6 4}
but it outputs 1 and not 55 as expected

Your problem is this line:
set $max $i
In Tcl, the $ character means read from the named variable and use that value as (possibly part of) the argument to a command. It means this always. No exceptions (unless backslash-quoted or in {braces}, of course). Thus, on the first iteration of the loop you're getting (after substitution):
set 1 12
The variable name 1 is legal, but unusual and not what you want. For the algorithm to work, you instead want to give the set command the name of the variable to set, max, resulting in this:
set max $i
which will be substituted to this on the first iteration:
set max 12
That looks right! A good rule of thumb for future programming is that if a command manipulates a variable (setting it or updating it) then you need to pass the name of the variable, not the value retrieved from it.
The standard method of getting the maximum value of a list is a one-liner:
set max [tcl::mathfunc::max {*}$items]
This uses the built-in function max (which is in the ::tcl::mathfunc namespace) and passes the contents of the list in items as multiple arguments, all as one step. The {*}$ sequence is a combination of the variable-read syntax rule with the list-expansion rule, which you've probably not really thought about yet. However it's still a good exercise to write your own maximum finder as a learning exercise.

Related

Use of function / return

I had the task to code the following:
Take a list of integers and returns the value of these numbers added up, but only if they are odd.
Example input: [1,5,3,2]
Output: 9
I did the code below and it worked perfectly.
numbers = [1,5,3,2]
print(numbers)
add_up_the_odds = []
for number in numbers:
if number % 2 == 1:
add_up_the_odds.append(number)
print(add_up_the_odds)
print(sum(add_up_the_odds))
Then I tried to re-code it using function definition / return:
def add_up_the_odds(numbers):
odds = []
for number in range(1,len(numbers)):
if number % 2 == 1:
odds.append(number)
return odds
numbers = [1,5,3,2]
print (sum(odds))
But I couldn’t make it working, anybody can help with that?
Note: I'm going to assume Python 3.x
It looks like you're defining your function, but never calling it.
When the interpreter finishes going through your function definition, the function is now there for you to use - but it never actually executes until you tell it to.
Between the last two lines in your code, you need to call add_up_the_odds() on your numbers array, and assign the result to the odds variable.
i.e. odds = add_up_the_odds(numbers)

Node-red - need a multi-input function for a number value

So I'm just getting to grips with node-red and I need to create a conditional global function.
I have two separate global.payloads set to a number value of either 0 or 1.
What I need to happen now is, if global.payload is equal to value 1 then follow this flow, if it is equal to value 0 then follow this one.
I'm just a little confused with the syntax for the function statement. Any help gratefully appreciated.
Since you haven't accepted the current answer, thought I'd give this a try.
I think this is what you need to handle inputs from two separate global contexts. I'm simulating them here with two separate inject nodes to demonstrate:
The checkconf inject node emits a 1 or a 0. Same for the meshstatus node. Substitute your real inputs for those inject nodes. The real work is done inside the function:
var c = context.get('c') || 0; // initialize variables
var m = context.get('m') || 0;
if (msg.topic == "checkconf") // update context based on topic of input
{
c = {payload: msg.payload};
context.set("c", c); // save last value in local context
}
if (msg.topic == 'meshstatus') // same here
{
m = {payload: msg.payload};
context.set('m', m); // save last value in local context
}
// now do the test to see if both inputs are triggered...
if (m.payload == 1) // check last value of meshstatus first
{
if (c.payload == 1) // now check last value of checkconf
return {topic:'value', payload: "YES"};
}
else
return {topic:'value', payload: "NO"};
Be sure to set the "topic" property of whatever you use as inputs so the if statements can discriminate between the two input. Good luck!
You can use the Switch node to do this, rather than a Function node.

How to do pivot table without knowledge of columns

I have read most of the posts on stackoverflow on how to do a pivot table but all of the posts show examples with prior knowledge of the columns. How do you construct a query if you have no knowledge of what the columns will be. here is some sample data:
id column value Row
1 age 13 1
2 height 55 1
3 fav number NULL 1
4 siblings 4 1
5 age 55 2
6 height 54 2
7 fav number 12 2
I am looking for this output:
row age height fav number siblings
1 13 55 NULL 4
2 55 54 12 NULL
As you can see there is no row 2 is missing an entry for siblings. The column names are unknown at the time of the query. How would you make this query.
I don't see any way you can just write some fancy SELECT query to get what you want. You're going to have to do some pre-processing.
You have to be executing this MySQL query from some sort of program, application, script, etc. Not sure what the language is, but here's what I would do in PHP:
/* $data is where our data is going to be stored in our desired format */
$data = array();
/* $columns is a list of all column names */
$columns = array();
/* $rows is a list of all row names (probably '1', '2', etc) */
$rows = array();
$result = mysql_query('SELECT column, value, row FROM TableName');
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
/* if this row isn't in $data yet, add it */
if (!array_key_exists($row['row'], $data) {
$data[$row['row']] = array();
}
/* if this column isn't in $columns yet, add it */
if (!in_array($row['column'], $columns)) {
array_push($columns, $row['column']);
}
/* if this row isn't in $rows yet, add it */
if (!in_array($row['row'], $rows)) {
array_push($rows, $row['row']);
}
/* set the actual value in our multi-dimensional array $data */
$data[$row['row']][$row['column']] = $row['value'];
}
/* free the result (php specific thing) */
mysql_free_result($result);
/* if we didn't set anything (row, column) pairs, set it to null in $data */
foreach ($rows as $r) {
foreach ($columns as $c) {
if (!array_key_exists($c, $data[$r])) {
$data[$r][$c] = null;
}
}
}
This will put all the data into a format you want in an array in PHP.
For example, after running this algorithm on the sample data you provided above, you would be able to do:
echo $data['2']['age']; // $data['row']['column']
Which would output 55.
OR if your database isn't being updated in real time (you have a bunch of data that you'd like to reformat once, rather than continuously), you could extend the script above to also have some "CREATE TABLE", "INSERT INTO" queries that basically recreate the table in the format you're looking for.
Furthermore, if you ARE receiving data in realtime, you can still write the script described above, but you'd just want to remove the rows from the original table as you processed them, and then just run the script whenever data is being put into the original table.
I doubt that you can do this in MySQL or PostgreSQL as you expect to, however there is an alternative which I have used where data is very free-form. Our use case is "attributes that the menu items pass back to the application" and of course we have no knowledge of these in the query. But you can't create a simple pivot table.
The reason you can't is that PostgreSQL requires that the tuple structure returned to be defined in advance. Not all db's do this (Informix for example, allows different rows to have different structures!) but most do.
Our approach is PostgreSQL-only. However, maybe with some tweaking you can find a MySQL equivalent somewhere. What we did would basically be in your version:
select row, array_agg("column" || '=' || "value") from sample_data group by row;
This produces output like:
1 {"age=3","height=55",null,"siblings=4"}
2 {"age=55","height=54","favorite_number=12"}
You can even get rid of the NULLS by:
select row, array_agg("column" || '=' || "value")
WHERE value is not null
GROUP BY row;
Then you get something like:
1 {"age=3","height=55","siblings=4"}
2 {"age=55","height=54","favorite_number=12"}
I don't know how to do the same in MySQL though.

LINQ-to-SQL oddity with multiple where clause arguments on the same field

My problem requires me to dynamically add where clauses to a IQueryable based on user input. The problem i'm having is that Linq-to-SQL doesn't seem to like having multiple where clauses on the same field, it actually duplicates the search arg value for the last item on all parameters. I verified this behavior through a SQL trace. Here is what I'm seeing.
WHERE ([t22].[OpenText] LIKE #p11) AND ([t22].[OpenText] LIKE #p12)
-- #p11: Input NVarChar (Size = 10; Prec = 0; Scale = 0) [%classify%] // Should be 2da57652-dcdf-4cc8-99db-436c15e5ef50
-- #p12: Input NVarChar (Size = 10; Prec = 0; Scale = 0) [%classify%]
My code uses a loop to dynamically add the where clauses as you can see below. My question is how do I work around this? This pretty much seems like a bug with the tool, no?
// add dyanmic where clauses based on user input.
MatchCollection searchTokens = Helper.ExtractTokensWithinBracePairs(filterText);
if (searchTokens.Count > 0)
{
foreach(Match searchToken in searchTokens)
query = query.Where((material => material.OpenText.Contains(searchToken.Value)));
}
else
{
query = query.Where((material => material.OpenText.Contains(filterText)));
}
Closing over the loop variable considered harmful! Change
foreach(Match searchToken in searchTokens) {
query = query.Where(
material => material.OpenText.Contains(searchToken.Value)
);
}
to
foreach(Match searchToken in searchTokens) {
Match token = searchToken;
query = query.Where(
material => material.OpenText.Contains(token.Value)
);
}
You are closing over the loop variable, which is considered harmful. To fix do this:
foreach(Match searchToken in searchTokens)
{
Match searchToken2 = searchToken;
// ^^^^^^^^^^^^ copy the value of the reference to a local variable.
query = query.Where(material => material.OpenText.Contains(searchToken2.Value));
// use the copy here ^^^^^^^^^^^^
}
The reason why your version doesn't work is that the query refers to the variable searchToken, not the value it had when the query was created. When the variable's value changes, all your queries see the new value.
I don't have enough rep to leave comments yet (or this would be a comment and not an answer) but the answers listed here worked for me.
However, I had to turn off compiler optimizations in order for it to work. If you do not turn off compiler optimizations (at least at the method level) then the compiler sees you setting a loop variable to a local variable and throws the local variable away.

Why does my use of Perl's split function not split?

I'm trying to split an HTML document into its head and body:
my #contentsArray = split( /<\/head>/is, $fileContents, 1);
if( scalar #contentsArray == 2 ){
$bodyContents = $dbh->quote(trim($contentsArray[1]));
$headContents = $dbh->quote(trim($contentsArray[0]) . "</head>");
}
is what i have. $fileContents contains the HTML code. When I run this, it doesn't split. Any one know why?
The third parameter to split is how many results to produce, so if you want to apply the expression only once, you would pass 2.
Note that this does actually limit the number of times the pattern is used to split the string (to one fewer than the number passed), not just limit the number of results returned, so this:
print join ":", split /,/, "a,b,c", 2;
outputs:
a:b,c
not:
a:b
sorry, figured it out. Thought the 1 was how many times it would find the expression not limit the results. Changed to 2 and works.