this may be a simple question, yet I haven't been able to find an answer to it:
How do I add a value to an array without overwriting (all) old values, or having to rewrite them? Is there such a thing as array_push in LUA? And if so, does it work for multidimensional arrays as well?
Example:
Array={"Forest","Beach","Home"} --places
Array["Forest"] = {"Trees","Flowers"} --things you find there
Array["Forest"]["Trees"] = "A tree is a perennial woody plant" --description
If I'd like to add a description of a new thing in a new place, I can't do it using
Array["Restaurant"]["Spoon"] = "A type of cutlery."
because I'd have to declare all these things, as well as the old ones so I don't overwrite them. So I'm looking for something like:
array_push(Array, "Restaurant")
array_push(Array["Restaurant"],"Spoon")
Array["Restaurant"]["Spoon"] = "A type of cutlery."
Thanks!
The following index metamethod implementation should do the trick.
local mt = {}
mt.__index = function(t, k)
local v = {}
setmetatable(v, mt)
rawset(t, k, v)
return v
end
Array={"Forest","Beach","Home"} --places
setmetatable(Array, mt)
Array["Forest"] = {"Trees","Flowers"} --things you find there
Array["Forest"]["Trees"] = "A tree is a perennial woody plant" --description
Array["Restaurant"]["Spoon"] = "A type of cutlery."
Note that you are mixing array indexed values with with string indexed values, and I don't think you intended to do so. For example, your first line stores "Forest" under the key "1", while the second line creates a new table key "Forest" with a table value that holds sequential string values. The following code prints out the generated structure to demonstrate my meaning.
local function printtree(node, depth)
local depth = depth or 0
if "table" == type(node) then
for k, v in pairs(node) do
print(string.rep('\t', depth)..k)
printtree(v, depth + 1)
end
else
print(string.rep('\t', depth)..node)
end
end
printtree(Array)
Next is the resulting output of the two code snippets listed above.
1
Forest
2
Beach
3
Home
Restaurant
Spoon
A type of cutlery.
Forest
1
Trees
2
Flowers
Trees
A tree is a perennial woody plant
With this understanding, you could then solve your problem without such trickery as follows.
Array = {
Forest = {},
Beach = {},
Home = {}
}
Array["Forest"] = {
Trees = "",
Flowers = "",
}
Array["Forest"]["Trees"] = "A tree is a perennial woody plant"
Array["Restaurant"] = {
Spoon = "A type of cutlery."
}
printtree(Array)
The output is then what you probably expected.
Restaurant
Spoon
A type of cutlery.
Beach
Home
Forest
Flowers
Trees
A tree is a perennial woody plant
With all of that in mind, the following accomplishes the same thing, but is much clearer in my humble opinion.
Array.Forest = {}
Array.Beach = {}
Array.Home = {}
Array.Forest.Trees = ""
Array.Forest.Flowers = ""
Array.Forest.Trees = "A tree is a perennial woody plant"
Array.Restaurant = {}
Array.Restaurant.Spoon = "A type of cutlery."
printtree(Array)
First, what you're making is not an array at all, but a dictionary. Try:
T = { Forest = { } , Beach = { } , Home = { } }
T.Forest.Spoon = "A type of cutlery"
Otherwise table.insert may be what you want in array_push
This is almost identically there in standard Lua like this:
Array.Restaurant={}
Array.Restaurant.Spoon={}
Array.Restaurant.Spoon[1]="A type of cutlery."
the table.key notation is equivalent of the table["key"] notation.
Now every item has it's description in the value corresponding to a number-key, and sub items as values corresponding to string keys.
If you really want to have exactly the same syntax as your example, you'll have to use metatables (__index and __newindex methods).
Related
There are two simple problems here in initializing this structure.
One is the enunumerated value TS (I get error : Cannot convert value of type 'TournNames' to expected argument type 'TournamentName')
the other is initializing an array of strings (I get the error : Cannot convert value of type '[String]' to expected argument type 'TouramentScores'
Suppose I am trying to set up a structure to model the scores of tennis players and all of their matches in each of the major tournaments (just for fun). Each tournament has a name (e.g. Wimbledon) and a series of scores for that player (for example, in the opening match, their score might be "4-6, 6-4, 7-6, 6-2")... upto 7 matches in each tournament. Each player should have an array of four tournaments (names and array of scores), and eventually there should be an array of players. I am also trying to use enums not too successfully. Ideally, if I want to find how Roger Federer did in his third match of wimbledon this year, I would access something like player.tournament.wim.Roundof32 or something roughly like that. But before I can even get to playing with that, I can't seem to init dummy data for even a single tournament.
Any ideas? I don't think this is that hard of question but I just don't know each. See "*** this line" below for two lines that are problematic
// tournament name enum
enum TournNames : String {
case wim = "Wimbledom"
case fo = "French Open"
case ao = "Australian Open"
case uo = "US Open"
}
//
struct TournamentName {
var Tname : TournNames // = .wim
}
// This is the structure for a tournament score array with some dummy values.
struct TouramentScores {
var Match1 : String = "7-6, 6-4, 3-6, 7-6"
var Match2 : String = "7-6, 6-4, 3-6, 7-6"
}
// This is one entire Tournament record for one player = tournament name + array of scores ... the next goal but not used here until I get over these hurdles
struct TournamentResult {
var TournamentName : TournNames = .wim
var Scores : TouramentScores
}
// ... finally the structure of a player ...
struct DummyTennisPlayer {
var LastName : String // last name
var FirstName : String //first name
var TN : TournamentName
var TS : TouramentScores
// var WimRes : TournamentResult // to start a single tournament
// var SeasonResults : [TournamentResult] // ultimately should be an array of 4 tournaments
}
// trying to initialize some dummy data without success after trying several things
extension DummyTennisPlayer {
static var dummyResults : [DummyTennisPlayer] {
[
DummyTennisPlayer.init(
LastName : "Federer",
FirstName: "Roger",
TN : TournNames.wim // **** this line
,
TS : ["XX", "yy"] /// *** this line
)
]
}
}
As I think you're discovering, a simple series of nested types is unlikely to cut it here. As soon as you get to entities like players, tournaments, matches and lookups like "how Roger Federer did in his third match of wimbledon this year", you've become a candidate for using a database where you can manipulate one-to-many and many-to-many relationships. I can't tell you what database to use, and anyway that's a matter of opinion; from what you've said so far, SQLite would be sufficient (and I am personally not a fan of Core Data just for this kind of thing).
I guess your code is a kind of exercise, so before you go on later to Core Data or SQLite,
extension DummyTennisPlayer {
static var dummyResults: [DummyTennisPlayer] = [
DummyTennisPlayer(LastName: "Federer", FirstName: "Roger", WimbledomResult: TournamentResult(Scores: TouramentScores()))
]
}
should answer your question.
1 - To initialize a Swift struct, use the following syntax:
MyStruct(property1: "my property1 value", property2: "my property2 value")
2 - the tournament name property in TournamentResult is already set to .wim so you just need to initialize the Scores. As your TournamentScores properties are already all set, you just need to pass an instance of TournamentScores() to TournamentResult(Scores:).
By the way, only use lowercases for the first letter of the name of your variables or struct properties: var lastName or TournamentResult(scores:).
I think you are confusing the term "multi-dimensional (array) structures" (which are just arrays nested inside other arrays, like that: [ [1, 2, 3], [2, 3, 4], [3, 4, 5]]) with the struct objects. You are probably not supposed to use structs so extensively here.
Don't hesitate to review the way you decide to use enums, structs, or arrays. Your code may work but will be difficult to read and use (example: how would you access a specific set score if you put all of the set scores in a single String? Why not use an array?)
I want to make a multi choice question program in SML.
I have a text file whose content is structured as follows:
category1:Basic sml/sql
1-How many subsets does the power set of an empty set have?
A) One
B) Two
C) Three
D) Zero
Ans:A
2-What is the cardinality of the set of odd positive integers less than 10?
A) 20
B) 3
C) 5
D) 10
Ans:C
Each category has more than 5 questions
Each question belongs to a category
For each question there are 4 proposed answers followed by the answer on another line
I would like, to be able to retrieve and display to the user just the question (a random question) and the corresponding proposed answers. I have written this function that allows me to retrieve line by line all the content of my file. But I'm still stuck on how to unload a question-answer block.
fun getFromFile(file_name) =
let
val file = TextIO.openIn file_name
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
String.tokens (fn c => c = #"\n") text
end
val table = getFromFile("question_file.txt");
How could I proceed? Is it possible to retrieve the lines of the file without passing them through a table first (retrieve the text directly)?
I'm still stuck on how to unload a question-answer block.
How could I proceed?
Find a way to encode multiple categories that contain multiple questions that contain multiple answers each. And once you have found a way to store that to a file (a file format), write a decoder. Your current decoder is a line decoder. You can encode things within things within things using lines, but you can also do it other ways.
For example, using JSON:
[
{
"category": "Basic sml/sql",
"questions": [
{
"question": "How many subsets does the power set of an empty set have?",
"answers": [
{ "answer": "Zero", "correct": false },
{ "answer": "One", "correct": true },
{ "answer": "Two", "correct": false },
{ "answer": "Three", "correct": false }
]
},
...
]
},
...
]
If relying on third-party libraries seems too difficult, you could come up with a file format yourself, e.g. a line-based one:
CATEGORY Basic sml/sql
QUESTION How many subsets does the power set of an empty set have?
ANSWER Zero
ANSWER_CORRECT One
ANSWER Two
ANSWER Three
QUESTION ...
So given your line-based reader, loop over each line and look at the first word:
If it's CATEGORY, start a new category.
If it's QUESTION, start a new question within the current category.
If it's ANSWER or ANSWER_CORRECT, provide an option in the current question within the current category.
This suggests a recursive function (since it needs to go over each line) that takes a number of parameters: The current category, the current question, and the total set of categories, questions and answers so far.
At this point you probably have to think: How do I store categories of questions with multiple answers in memory? What data type should I be using? E.g. using type aliases, you could express your data model like this:
type answer_option = string * bool
val example_answer_option = ("Zero", false) : answer_option
type question_answers = string * answer_option list
val example_question_answers =
("How many subsets does the power set of an empty set have?",
[
("Zero", false),
("One", true),
("Two", false),
("Three", false)
]
) : question_answers
type category = string * question_answers list
val example_category =
("Basic sml/sql",
[ example_question_answers ]
) : category
val example_categories = [ example_category ] : category list
The way SML type aliases work is that you get all of those expanded into the primitive types they consist of, so they may show up in your REPL as such:
> type answer_option = string * bool
type question_answers = string * (string * bool) list
type category = string * (string * (string * bool) list) list
which is considerably less readable and is one reason to use alternatives like datatype, abstype or opaque modules.
Going with this, however, you may define a stub like:
fun parse (line::lines, currentQuestion, currentAnswers, currentCategory, acc) =
case splitFirstWord line of
("CATEGORY", cat) => ...
| ("QUESTION", q) => ...
| ("ANSWER", aWrong) => ...
| ("ANSWER_CORRECT", aRight) => ...
| _ => raise Fail ("Unknown: " ^ line)
Now there are two sub-problems:
splitFirstWord doesn't actually exist (yet).
There is a whole lot of book-keeping of current state.
Good luck!
Is it possible to retrieve the lines of the file without passing them through a table first (retrieve the text directly)?
I don't really understand this question. Unarguably, yes?
If by "table" you mean some kind of indexable data structure like a list:
Just don't call String.tokens (fn c => ...) on the input.
This gives you a basic string.
Note that table is just the name of a value binding.
If you like, you can pass it through a chair instead:
fun getFromFile(file_name) =
let
val file = TextIO.openIn file_name
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
text
end
val chair = getFromFile "question_file.txt"
Note also that the parenthesis around function arguments is not necessary in SML. In fact, if you think they are, you'll probably make a syntax mistake soon enough. Try to avoid redundant syntax for greater clarity.
I am trying to assign multiple values for one variable in a table. One for the string name, and one for an integer. The code would go:
items = {
potion = "Potion", 100
}
I do not know how to formally write this, and how to call for those specific values.
(Do you call it like this?)
io.write(item.potion.1) --> Potion
io.write(item.potion.2) --> 100
(Or something else?)
Please help. :I
You can assign those values to a table indexed by numbers or identifiers:
-- identifiers
items = {
potion = {name = "Potion", value = 100},
}
print(items.potion.name, items.potion.value)
-- numeric indexes
items = {
potion = {"Potion", 100},
}
print(items.potion[1], items.potion[2])
I personally prefer the former approach (as it's more maintainable, even though a bit more verbose), but either one should work.
Lua allows for multiple assignments to multiple variables.
like so:
potion, value = "Potion", 100
but this can not be done inside a table definition.
items = {
potion = "Potion", 100
}
What your code here is doing is setting potion to the value "Potion" and then the , ends the assignment. The next assignment is 100 which will be assigned to a default key, in this case 1.
In side a table you end each assignment with a , so your tables contents are equal to:
items = {
potion = "Potion",
[1] = 100
}
To accomplish the desired behavior you can nest tables:
items = {
potion = {
"Potion",
100
}
}
This example can be accessed like items.potion[1] not like items.potion.1 this is because the . notation can't be used with a key that begins with a number.
Let's say I have an array of strings and I want to get a list with objects that match, such as:
var locales=Locale.getAvailableLocales()
val filtered = locales.filter { l-> l.language=="en" }
except, instead of a single value I want to compare it with another list, like:
val lang = listOf("en", "fr", "es")
How do I do that? I'm looking for a one-liner solution without any loops. Thanks!
Like this
var locales = Locale.getAvailableLocales()
val filtered = locales.filter { l -> lang.contains(l.language)}
As pointed out in comments, you can skip naming the parameter to the lambda, and use it keyword to have either of the following:
val filtered1 = locales.filter{ lang.contains(it.language) }
val filtered2 = locales.filter{ it.language in lang }
Just remember to have a suitable data structure for the languages, so that the contains() method has low time complexity like a Set.
I have been working on a project and Stack Overflow has helped me with a few problems so far, so I am very thankful!
My question is this:
I have an array like this:
var records:Object = {};
var arr:Array = [
records["nh"] = { medinc:66303, statename:"New Hampshire"},
records["ct"] = { medinc:65958, statename:"Connecticut"},
records["nj"] = { medinc:65173, statename:"New Jersey"},
records["md"] = { medinc:64596, statename:"Maryland"},
etc... for all 50 states. And then I have the array sorted reverse numerically (descending) like this:
arr.sortOn("medinc", Array.NUMERIC);
arr.reverse();
Can I call the name of the record (i.e. "nj" for new jersey) and then get the value from the numeric position above and below the record in the array?
Basically, medinc is medium income of US states, and I am trying to show a ranking system... a user would click Texas for example, and it would show the medinc value for Texas, along with the state the ranks one position below and the state that ranks one position above in the array.
Thanks for your help!
If you know the object, you can use the array.indexOf().
var index:int = records.indexOf(records["nj"]);
var above:Object;
var below:Object;
if(index + 1 < records.length){ //make sure your not already at the top
above = records[index+1];
}
if(index > 0){ //make sure your not already at the bottom
below = records[index-1];
}
I think this is the answer based on my understanding of your data.
var index:int = arr.indexOf(records["nh"]);
That will get you the index of the record that was clicked on and then for find the ones below and above just:
var clickedRecord:Object = arr[index]
var higherRecord:Object = arr[index++]
var lowerRecord:Object = arr[index--]
Hope that answers your question
Do you really need records to be hash?
If no, you can simply move key to record field and change records to simple array:
var records: Array = new Array();
records.push({ short: "nh", medinc:66303, statename:"New Hampshire"}),
records.push({ short: "ct", medinc:65958, statename:"Connecticut"}),
....
This gives you opportunity to create class for State, change Array to Vector and make all of this type-safe, what is always good.
If you really need those keys, you can add objects like above (with "short" field) in the same way you are doing it now (maybe using some helper function which will help to avoid typing shortname twice, like addState(records, data) { records[data.short] = data }).
Finally, you can also keep those records in two objects (or an object and an array or whatever you need). This will not be expensive, if you will create state object once and keep references in array/object/vector. It would be nice idea if you need states sorted on different keys often.
This is not really a good way to have your data set up - too much typing (you are repeating "records", "medinc", "statename" over and over again, while you definitely could've avoided it, for example:
var records:Array = [];
var states:Array = ["nh", "ct", "nj" ... ];
var statenames:Array = ["New Hampshire", "Connecticut", "New Jersey" ... ];
var medincs:Array = [66303, 65958, 65173 ... ];
var hash:Object = { };
function addState(state:String, medinc:int, statename:String, hash:Object):Object
{
return hash[state] = { medinc: medinc, statename: statename };
}
for (var i:int; i < 50; i++)
{
records[i] = addState(states[i], medincs[i], statenames[i], hash);
}
While you have done it already the way you did, that's not essential, but this could've saved you some keystrokes, if you haven't...
Now, onto your search problem - first of all, true, it would be worth to sort the array before you search, but if you need to search an array by the value of the parameter it was sorted on, there is a better algorithm for that. That is, if given the data in your example, your specific task was to find out in what state the income is 65958, then, knowing that array is sorted on income you could employ binary search.
Now, for the example with 50 states the difference will not be noticeable, unless you do it some hundreds of thousands times per second, but in general, the binary search would be the way to go.
If the article in Wiki looks too long to read ;) the idea behind the binary search is that at first you guess that the searched value is exactly in the middle of the array - you try that assumption and if you guessed correct, return the index you just found, else - you select the interval containing the searched value (either one half of the array remaining) and do so until you either find the value, or check the same index - which would mean that the value is not found). This reduces asymptotic complexity of the algorithm from O(n) to O(log n).
Now, if your goal was to find the correspondence between the income and the state, but it wasn't important how that scales with other states (i.e. the index in the array is not important), you could have another hash table, where the income would be the key, and the state information object would be the value, using my example above:
function addState(state:String, medinc:int, statename:String,
hash:Object, incomeHash:Object):Object
{
return incomeHash[medinc] =
hash[state] = { medinc: medinc, statename: statename };
}
Then incomeHash[medinc] would give you the state by income in O(1) time.