Variable value in foreach of Red language - loops

I am using following code to add multiple GUI elements to a view via a foreach loop:
myRange: function [n][ ; to produce a vector of [1 2 3 4... n]
vv: make vector! n
i: 1
foreach j vv [
vv/:i: i
i: i + 1
if i > n [break]]
vv ]
view collect[
foreach i myRange 10 [
print append "i in loop: " i
keep [ t: text ] keep append "message number: " i
keep [field "entry" button "Click" [t/text: "clicked"] return]
] ]
All GUI elements are being produced. But the code append "message number: " i is showing value of i to be 12345678910 in all text elements and not 1, 2, 3... 10 for different text elements.
Also, print append... statement is producing following output:
i in loop: 1
i in loop: 12
i in loop: 123
i in loop: 1234
i in loop: 12345
i in loop: 123456
i in loop: 1234567
i in loop: 12345678
i in loop: 123456789
i in loop: 12345678910
Moreover, clicking any button changes text of only the last added text element.
Where is the problem and how can it be solved? Thanks for your help.
Edit: It seems the language is converting my code from:
for i 1 10 1 [
print append "i in loop: " i ]
to:
a_variable: "i in loop"
for i 1 10 1 [
print append a_variable i ]
Which is not what I and (I think) most users want. In most languages a string "i in loop" will be taken as a constant and not converted to a variable since user has not specified it so. IMHO it will be easier for users of other languages to come here if such basic conventions are not altered.

Whenever you see something like this, it means you failed to create a new series and are reusing an existing series.
To get around that you need to create a new series with copy
Eg.
print append copy "i in loop: " i
Rebol3/ren-c no longer has this problem because source code is immutable and so you will get an error message with such code.

Rebol and Red reuses series (e.g. strings, blocks) as much as possible if you do not tell them otherwise by initializing anew with copy, make etc. So your issue should go away if you write
append copy "message number: " i

As the other answers suggest, you are only using a single string for your message and would need to be copied.
As to the other concern—you should take a wee look at the code you're generating (as I suggested elsewhere, you can pop a little PROBE to examine the output of the COLLECT function):
[
t: text "message number: 1" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 2" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 3" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 4" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 5" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 6" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 7" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 8" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 9" field "entry" button "Click" [t/text: "clicked"] return
t: text "message number: 10" field "entry" button "Click" [t/text: "clicked"] return
]
As you can see, you're constantly reassigning t so that in the end, it only refers to the last face.
A few options exist here—most prominent is to generate the name you're assigning the text face to. Within your FOREACH loop:
keep compose/deep [
(to set-word! rejoin ["t-" i])
text (rejoin ["Message Number: " i])
field "entry"
button "Click" [
set in (to word! rejoin ["t-" i]) 'text "clicked"
]
return
]
Note that in order to simplify the block creation, I used this line:
set in (to word! rejoin ["t-" i]) 'text "clicked"
This composes to (in the first instance):
set in t-1 'text "clicked"
IN returns the given word ('text) bound to the given context (the face object t-1) which is then SET to "clicked".
UPDATE
This method doesn't even use a word name, just uses a common parent to connect the button to the label:
view collect [
keep [below space 0x0]
foreach i myRange 10 [
keep compose/deep [
panel [
origin 0x0
text (rejoin ["Message Number: " i])
field "entry"
button "Click" [face/parent/pane/1/text: "clicked"]
]
]
]
]

Related

How to set combo-box as default

How to clear a combo-box selected values when clicking on a 'Clear' button? When the time of clicking clear button, it needs to set it as default.
I wrote it as;
DO:
ASSIGN
coCombo-2:SCREEN-VALUE IN FRAME {&FRAME-NAME} = "".
coCombo-3:SCREEN-VALUE IN FRAME {&FRAME-NAME} = "".
END.
"But this is not working."
You need to assign a space instead of an empty string
DO:
ASSIGN
coCombo-2:SCREEN-VALUE IN FRAME {&FRAME-NAME} = " ".
coCombo-3:SCREEN-VALUE IN FRAME {&FRAME-NAME} = " ".
END.
see also https://knowledgebase.progress.com/articles/Article/P21248

Watson Conversation get the first key of every element of an array

I am currently working on a chatbot based on IBM Watson conversations.
And I'm struggling getting the key out of the enginePower in the filter.
I need every key to be displayed in the chat. So a user can select one.
The structure looks like that:
"filter": {
"enginePower": [{
"key": "55",
"text": "55 kW (75 PS)",
"selectable": false
},
{
"key": "65",
"text": "65 kW (88 PS)",
"selectable": false
},
{
"key": "66",
"text": "66 kW (90 PS)",
"selectable": false
},
{
"key": "81",
"text": "81 kW (110 PS)",
"selectable": false
}]
}
Thanks in advance.
Short answer:
Your key values are: <? T(String).join( ",", $filter.get('enginePower').![key] ) ?>
Broken down
$filter.get('enginePower')
This will return the enginePower as an array containing all your objects.
.![key]
This will return the "Key" field values as an array.
T(String).join( ",", XXX )
This will convert your array XXX into a comma delimited list.
So your final output will say:
Your key values are: 55,65,66,81
Just to add to this. You can only get a single key/value list. If you want to capture a value of a different attribute using a key lookup, then you need to loop.
So for example, start by setting a counter to 0. Then have your node check for $filter.enginePower.size() > $counter.
Within that node, you set "multiple responses" on. Then for the first condition you set $filter.enginePower[$counter].selectable == true. This will allow you to take action if that field is true.
After this you need to create a child node, and have the parent node jump to it. Within the child node response put <? $counter = $counter + 1 ?>. Lastly have the child node jump back to the parent node.
This will loop through the array. Warning! You can only loop 50 times before the loop will end. This is to stop potential endless loops.
Realistically though, you can easily solve all this by formatting the data correctly at the application layer.

Array of Int and String: Update only Int value?

I'm trying to make a chore app for (my) kids. I´m using a tableView with custom cells. For now, I have a fixed list (array) with different chores, and a doneButton for when the chore is done. I also have a label that counts the times that chore is done.
I also have a second View Controller that is used to sum up all the chores that is done, and the amount of times they are done.
So, when the doneButton on the chore "do dishes" is pressed, the second View Controller should show: 1 Do dishes.
If the done button on "do dishes" is pressed again, the second View Controller should show: 2 Do dishes.
And when the doneButton on the chore "walk dog" the second View controller should show:
2 Do dishes.
1 Walk dog.
My problem is that I cannot get the list in the second View Controller right. When the doneButton is pressed, its either overwriting the list, or adding like this: 1 Do dishes. 2 Do dishes 1 Walk dog.
Im trying to use a array, and append to this, but I can't figure out how to update only the Int, and not the String.
This is where I'm at:
When doneButton is pressed:
summaryArray.append(String(choreList[indexPath.row].count) + " " + choreList[indexPath.row].chore)
How can I update the count in the array without doing anything else to the array?
You should use a struct like so:
struct Chores {
let choreName : String
let choreCounter : Int
}
Then you can fill an array with all the items you need like so:
var array = [Chores(choreName : "Walk Dog", choreCounter : 0),//etc. you can add more]
With this method you can change each individual item in the array:
array[0].choreName = "Walk Dog faster!"
array[0].choreCounter += 1

Refactor by defining method for Array of Hashes

I am trying to refactor my code by defining a conditional method.
The current code functions perfectly, but I need to refactor it thoroughly while maintaining the existing structure and readability.
In this scenario, the user is asked to make a selection. That selection is associated with a symbol in an array of hashes. The method should replace the last two puts in this sample below. The code prints these two lines over and over.
puts"Red, white, or something else?"
user_category_selection = gets.chomp.downcase
puts "-"*80
wine_rack = [
{ name: "The Fugitive",
vintage: 2010,
category: "Red",
grape_composition: "Red Blend",
location: "Napa, California",
personal_rating: 91},
{ name: "Au Bon Climat",
vintage: 2010,
category: "White",
grape_composition: "Chardonnay",
location: "Santa Barbara, California",
personal_rating: 89},
{ name: "Laurent-Perrier",
vintage: "Non-vintage",
category: "Something Else",
grape_composition: "Ultra Brut",
location: "France",
personal_rating: 92}
]
This is the piece that is not functioning:
def vintage_name_location(category)
category = wine_rack[:category].downcase
while category.downcase.include? user_category_selection
puts "#{wine[:vintage]} #{wine[:name]}, #{wine[:location]}".center(80)
puts "#{wine[:grape_composition]}, rated #{wine[:personal_rating]} points".center(80)
end
end
puts vintage_name_location(user_category_selection)
The rest of the code is:
until ["white", "red", "something else"].include? user_category_selection.downcase
puts "Pardon me for asking, but are you intoxicated already?".center(80)
puts "I said red, white, or something else.".center(80)
user_category_selection = gets.chomp
end
if user_category_selection.downcase.eql? "red"
puts "May I suggest one of these delightful reds:"
puts "--------------------------------------------------".center(80)
for wine in wine_rack
if wine[:category].downcase == user_category_selection
puts "#{wine[:vintage]} #{wine[:name]}, #{wine[:location]}".center(80)
puts "#{wine[:grape_composition]}, rated #{wine[:personal_rating]} points".center(80)
puts "--------------------------------------------------".center(80)
end
end
elsif user_category_selection.downcase.eql? "white"
As you've correctly described, your wine_rack is an Array so you can't look up items using the name of a category like this: wine_rack[:category].
One approach would be to use select to pick out the wines matching a category like this:
matching_wines = wine_rack.select { |wine| wine[:category].downcase == category }
You'd wrap that in your function like this:
def wines_in_category(category)
wine_rack.select { |wine| wine[:category].downcase == category }
end
and then use it like this:
suitable_wines = wines_in_category(user_category_selection)
for wine in suitable_wines
...
This is another solution that points out what variables you have to work with.
Notice that you don't have the ability to interact with wine_rack directly from the method. You can use the send method to open the scope gate, or you can just pass in the wine_rack array.
Also, Array#select will return all the instances in which the block returns true. This may not affect your software now, but it could lead to issues later:
puts "Variables I have to work with: #{local_variables}"
def get_wine_information(passed_wine_rack,user_selection)
puts "Variables I have to work with inside method: #{local_variables}"
selection = passed_wine_rack.find {|wine| wine[:category] == user_selection}
puts "#{selection[:vintage]} #{selection[:name]}, #{selection[:location]}"
puts "#{selection[:grape_composition]}, rated #{selection[:personal_rating]} points"
end
#Scope Gate
Kernel.send(:define_method, :get_wine_info) do |user_selection|
puts "Variables I have to work with inside scope gate: #{local_variables}"
end
#Calling just to see scope information
get_wine_info("")
get_wine_information(wine_rack,user_category_selection)

IUP, button, tree, matrix

1)
I try to load ico image to IUPbutton but with no success.
After linking IM dll's and add proper header this is my approach:
Ihandle *btn1, *btn2;
Ihandle* img1;
btn1 = IupButton("", NULL);
IupSetAttribute(btn1, "MINSIZE", "24x24");
btn2 = IupButton("", NULL);
IupSetAttribute(btn2, "MINSIZE", "24x24");
img1 = IupImage(16, 16, IupLoadImage("icons\\home_16x16.ico"));
IupSetAttribute(btn1, "IMAGE", "img1");
frame = IupHbox(btn1, btn2, NULL);
dlg = IupDialog(IupHbox(mat, IupVbox(frame, tree, NULL), NULL));
IUP don't report any error but image don't appear on the button btn1.
How to load picture from file to a button in RGBA mode?
2)
I fill IupTree with data from sqlite database in following order: 'Name' (which is root) and then about 170 branches which have 1-10 leafs. VALUE is set to 0 and 'Name' is selected.
How can I get with code expanded tree to first branches like when I doubleclick to 'Name'?
I try EXPANDALL attribute but then all leaf's are expanded too what is not wanted.
3)
How can I get IUPtree item 'id' in k_any callback f. e. when press ENTER key?
4)
How can I get IUPtree item text from 'id' in executeleaf and branchopen callbacks?
5)
How can I loop through IUPtree to get id, text, sort of item (branch/leaf)?
6) Is here a way on IUPmatrix to catch keyUP or keyRELEASED event like we get keyPRESS in K_ANY?
1) Pay more attention to the data type of each function. Notice that IupLoadImage already returns an Ihandle. So instead of:
img1 = IupImage(16, 16, IupLoadImage("icons\\home_16x16.ico"));
you should do this:
img1 = IupLoadImage("icons\\home_16x16.ico");
Also if you do this:
IupSetAttribute(btn1, "IMAGE", "img1");
you are specifying a string, you must somehow associate the string "img1" to the Ihandle img1. They are two very different things. Check the IupImage documentation. Or you do:
IupSetHandle("img1", img1);
IupSetAttribute(btn1, "IMAGE", "img1");
Or a better way:
IupSetAttributeHandle(btn1, "IMAGE", img1);
2) Have you try to expand only the branch you want expanded? Check the STATEid attribute on the IupTree documentation.
3) What you want is to the the item that has the focus. So get the VALUE attribute of the IupTree. Notice that the Enter key will trigger the executeleaf callback which already has the item id.
4) Check the TITLEid attribute in the documentation.
5) A tip, when setting/getting attributes of a IupTree, IupMatrix or IupList, you can use:
IupSetAttribute(ih, "TITLE3", "My Title");
or
IupSetAttributeId(ih, "TITLE", 3, "My Title");
6) As I told you before, IupMatrix inherits from IupCanvas, so you must also check for the IupCanvas callbacks. Check the IupMatrix callbacks documentation, at the end there is an explanation about the IupCanvas callbacks.

Resources