Confusion on "Immutable" lists in F# - f#

Totally newbie question.
I'm still trying to get a grasp on "Immutable" lists in F#.
Lets assume I have a "Visit" defined as:
module Visit =
type Model =
{
Name: string
}
let init row col cel =
{
Name = sprintf "My Name is row: %d col: %d cel: %d" row col cel
}
Now I define a "Cell" that may or may not have one visit:
module Cell =
type Model =
{
Visit: Visit.Model option
}
let setVisit m =
{ m with Visit = Some( Visit.init 9 9 9) }
and lastly I define a "Column" that has a list of cells:
module Column =
type Model =
{
Appointments: Cell.Model list
}
let updateCell (m:Model) =
let newList = m.Appointments |> List.mapi (fun index cell -> if index = 2 then Cell.setVisit cell else cell)
{m with Appointments = newList }
In the Column module, the "updateCell" function is wired to call Cell.setVisit for the 3rd cell. My intent is to replace the "Name" of the "Visit" held by the 3rd cell. My simple questions are:
Is this the correct way to do this?
If I am replacing the Appointments list, is this not changing the "Column" holding the Appointment List? (The Column is immutable, right? ).
Sorry for my confusion.
TIA

First: yes, this is an acceptable, if inefficient way of doing it for lists. Note that you're rebuilding the whole list on every updateCell call, even though most elements in it are the same.
I don't know how many appointments you expect to have in your model in practice. If it's significantly more than three, and if you're always updating the third one, it would be more efficient to cut the list, then glue it back together:
let newList = List.take 2 m.Appointments # [Cell.setVisit m.Appointments.[2]] # List.drop 3 m.Appointments
This way only the first three elements are rebuilt, and the tail of the list is reused.
However, if you need random-access operations, may I suggest using arrays instead? Sure, they're mutable, but they offer much better performance for random-access operations.
Second: no, the syntax { m with ... } does not change the Column. Instead, it creates a new column - a copy of m, but with all fields listed after with updated to new values.

Related

How to find requirements by keywords using DOORS DXL

I have identified 3-5 keywords for every requirement in module-A. Each keyword is separated by a comma. Now I want to search every requirement in module-B to see which of them have words that match each of the key words.
Not sure exactly what you're looking for. You might have to specify if none of these solutions I'm about to propose are exactly what you're looking for.
If you're trying to create a filter which displays only objects with those keywords in your current view, you can create an advanced filter by first going to filter (Tools > Filter > Define) and then select the Advanced button on the bottom left of the filter menu that appears.
At this point you can create custom rules for the filter. I would just create an individual rule for each word with the following definition:
Attribute: Object Text
Condition: contains
Value: <insert word here>
Match Case: uncheck
Regular Expression: uncheck
Then select the Add button to add the rule to the list of available rules in the Advanced Options.
At this point you can select multiple rules in the list of available rules and you can AND/OR these rules together to create a custom filter for the view that you want.
So that's for if you're trying to create a custom view with just objects containing specific words.
If you're talking about writing DXL code to automatically spit out requirements that have a particular word in it. You can use the something that looks like this:
Object o
String s
int offset, len
for o in entire (current Module) do
{
if (isDeleted(o)) continue
s = o."Object Text"""
if findPlainText(s, "<insert word here>", offset, len, false)
{
print identifier(o) // or replace this line with however you want to display object
}
}
Hope this is helpful. Cheers!
Edit:
To perform actions on a comma separated list, one at a time, you can use a while loop with some sort of search function that cuts off words one at a time.
void processWord (string someWord, Module mTarget, Object oSource)
{
Object oTarget
string objID = identifier(oSource)
for oTarget in mTarget do
{
if (<someWord in oTarget Object Text>) // edit function as necessary to do this
{
if (oTarget."Exists""" == "")
oTarget."Exists" = objID
else
oTarget."Exists" = oTarget."Exists" "," objID
}
}
}
int offset, len
string searchCriteria
Module mSource = read("<path of source module>", true)
Module mTarget = edit("<path of target module>", true)
Object oSource
for oSource in mSource do // can use "for object in entire(m)" for all objects
{
if (oSource != rqmt) continue // create conditions specific to your module here
searchCriteria = oSource."Search Criteria"""
while (findPlainText(searchCriteria, ",", offset, len, false))
{
processWord(searchCriteria[0:offset-1], mTarget, oSource)
searchCriteria = searchCriteria[offset+1:]
}
}
processWord(searchCriteria, mTarget, oSource) // for last value in comma separated list

converting string to table data type

I have some code, I want it to pick a random string from the list and convert it to a data type to be used in joypad.set() function.
Here is my code:
Buttons = { A = true,
B = true,
Down = true}
while (true) do
Random = math.random(3)
NewButton = (Buttons[Random])
joypad.set(1, (NewButton))
emu.frameadvance();
end;
You don't state your problem, but from your code, it looks like you're not getting the values from the array you expect. You're getting a random number between 1 and 3, but A, B, and Down are not 1, 2, and 3. Buttons is an associative array (key-value pairs) the way you declare it, so if you want to use it this way, you will need to set up a second array with just the key names, and get a random index from that, like so:
ButtonKeys = { "A", "B", "Down" }
Random = math.random(3)
NewButton = (Buttons[ButtonKeys[Random]])
This creates a table with the values of A, B, and Down as index 1, 2, and 3, so you use the random number to get the value from the ButtonKeys array, then use that value as the index for the Buttons array.
Edit: I reread the question and went over my original answer and realized I was thinking about you declaring the table differently. The way you declare the table, A, B, and Down become properties of Buttons, which you can access by calling them directly like Buttons.A, Buttons.B, and Buttons.C, or by using brackets with a string name of the property you want to access. In your case, Buttons["A"], Buttons["B"], and Buttons["Down"].

lua: user input to reference table

I am having trouble with my tables, I am making a text adventure in lua
local locxy = {}
locxy[1] = {}
locxy[1][1] = {}
locxy[1][1]["locdesc"] = "dungeon cell"
locxy[1][1]["items"] = {"nothing"}
locxy[1][1]["monsters"] = {monster1}
The [1][1] refers to x,y coordinates and using a move command I can successfully move into different rooms and receive the description of said room.
Items and monsters are nested tables since multiple items can be held there (each with their own properties).
The problem I am having is getting the items/monsters part to work. I have a separate table such as:
local monsters = {}
monsters["rat"] = {}
monsters["rat"]["Name"] = "a rat"
monsters["rat"]["Health"] = 5
monsters["rat"]["Attack"] = 1
I am using a table like this to create outlines for various enemy types. The monster1 is a variable I can insert into the location table to call upon one of these outlines, however I don't know how to reference it.
print("You are in ", locxy[x][y]["locdesc"]) -- this works
print("You can see a ", locxy[x][y]["monsters]["Name"],".") - does not work
So I would like to know how I can get that to work, I may need a different approach which is fine since I am learning. But I would also specifically like to know how to / if it possible to use a variable within a table entry that points to data in a separate table.
Thanks for any help that can be offered!
This line
locxy[x][y]["monsters]["Name"]
says
look in the locxy table for the x field
then look in the y field of that value
look in the "monsters"` field of that value
then look in the "Name" field of that value
The problem is that the table you get back from locxy[x][y]["monsters"] doesn't have a "Name" field. It has some number of entries in numerical indices.
locxy[x][y]["monsters][1]["Name"] will get you the name of the first monster in that table but you will need to loop over the monsters table to get all of them.
Style notes:
Instead of:
tab = {}
tab[1] = {}
tab[1][1] = {}
you can just use:
tab = {
[1] = {
{}
}
}
and instead of:
monsters = {}
monsters["rat"] = {}
monsters["rat"]["Name"] = "foo"
you can just use:
monsters = {
rat = {
Name = "foo"
}
}
Or ["rat"] and ["Name"] if you want to be explicit in your keys.
Similarly instead of monsters["rat"]["Name"] you can use monsters.rat.Name.

How to add a string automatically at the end of a table row

I have a table made of several rows and a variable number of columns in each rows.
If I want to add data in a new row, I just do
table[#table+1] = {['d1'] = data, ['d2'] = data, ... }
Now what I'd like to do is if I want to add to line 1 (for example):
table[1] = {['d' .. #columns+1] = data}
except that does not work and I can't find the solution.
My problem right now is that when my code adds data to an already existing row, it overwrites the existing data, which is not what I want.
For example this is currently the result for one line of the table:
-- Table: {4} { ["d3"]=154.04, },
instead of having a 'd1', 'd2' and finally 'd3' I just have 'd3'.
The code
table[1] = {['d' .. #columns] = data}
replaces the value at table[1] with the table on the right.
Try this instead:
table[1]['d' .. #columns] = data
For those who might have the same question, here is the closest answer I came to:
the # operator only counts integer keys. So # won't count any table containing string keys such as table[p1], which was my problem.
I solved it using the following function:
function tlength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
Now I can count any number of items in a column/subcolumn and add to it.
The problem I had with using numerical indices is that it makes the code unreadable, I didn't know what whas what. With this I can still use string indices and count them. It's a trade-off. One more function or the # operator.
Cheers

Can I make table from other table elements?

Am I able to make a separated table filled with elements from other tables in Lua? Like this:
TableA = { a=1, b=2, c=3 }
TableB = { John=4, Jane =5 }
TableC = { x = "asd", y = "dsa", z = "sda" }
TableAll = { TableA.a, TableB.John, TableC.x}
It's a general example, but may work. I'm not sure in it.
Is there some reason why that wouldn't work?
All that tablename.key does is access an element of a table. This is a value; the fact that it came from a table is completely irrelevant. The value returned by tablename.key is no different than the value 5, nil, true, "some string", or any other value in Lua.
You can initialize a table with values; it doesn't matter where those values come from.

Resources