List of tuples

Hello,

I want to create a list of tuples. A list because I can append data and tuples to maintain structure. In the the following code I split gkmax_block on new lines and the intent was to be able to split each line on ':' or space, return a tuple and append it to a list:

use List;

var gkmax_block = "Muffin-tin radius times maximum |G+k| : 7.000000000
using average radius
Maximum |G+k| for APW functions : 5.250000000
Maximum (1/2)|G+k|² : 13.78125000
Maximum |G| for potential and density : 12.00000000
Constant for pseudocharge density : 5
Radial integration step length : 4";

proc string2Tuple(s : string, delim : string) {
var poop = s.split(delim);
return (poop);
}
var t3 = string2Tuple(gkmax_block, '\n');
writeln(t3.size); /* no. of lines */

var things : list(tuple);
for i in 0..t3.size-1 {
var thing = string2Tuple(t3[i],' ');
things.pushBack(thing);
}

It does not work because "var things : list(tuple);" causes an error:

error: 'tuple' undeclared (first use this function)

Is there some workaround?

Thanks,
Roger

Hi Roger —

tuple is not a type in Chapel which is why you're getting this error. Tuple types in Chapel are written like (int, string, real) or (string, string, string) or 3*string, where the last is a shorthand for the previous, where the integer size must be known at compile time (a param expression in Chapel terms).

The other challenge here is that split()—as currently defined in Chapel at least—is an iterator, such that capturing it into an untyped variable as you do with poop here will generate an array of strings rather than a tuple of strings. This is because tuples must have known sizes + types in Chapel, but split() could generally return an arbitrary number of values. We have kicked around an proposal to support a tuple-based split in which the user would assert the size (or maximum size) of the tuple in the call, captured in this issue, but have not yet implemented it. That said, I think I could put together a routine that would do this for you in short order if that would be useful.

I guess the killer question for your use-case w.r.t. tuples is: Is there a fixed/expected/maximum number of elements that your split() would return?

Thanks,
-Brad

Hi Brad,

No, the expected number of elements is not fixed. Maybe I should use a list of lists instead?

Cheers,
Roger

Bummer! I wrote up the following example which does add a split overload that creates a tuple result: Run – Attempt This Online

I think if you can't guarantee the maximum number of elements, you'd want a list of lists. You could potentially use a list of arrays, though I think that might be harder to pull off (maybe I'll give that a shot next).

-Brad

Hi Roger —

Given your "lines won't have a maximal number of terms" characterization, I wrote a few variations of how you could handle this in Chapel:

  • The first one isn't a complete solution, but introduces an iterator pattern to read lines one at a time, process them, and yield the result: Run – Attempt This Online

  • The second modifies this iterator to store each line's words as a list of strings, yields those lists, and then stores the yielded lists into a list of lists of strings: Run – Attempt This Online

  • The third stemmed from my attempt to use a list of arrays, which I couldn't find a way to declare since each array has an unknown length. So what I did was create a record that wraps an array and created a list of that record type instead: Run – Attempt This Online

I don't mean to imply that any of these are the "correct" or best way to write this pattern in Chapel, just ways that occurred to me based on your original code and "no maximal number of strings" constraint.

-Brad

Hello Brad,

Thank you. I think I like the list-of-lists solution best but how do I call it on a string: linesToListsOfStrings() takes no args and is not a method? My naiive modification (below) produces an empty list.

Thanks again,
Roger

use IO, List;

iter linesToListsOfStrings(ref line : string) {
//var line: string;
while readLine(line) {
var words: list(string);
for word in line.split(" ") {
words.pushBack(word);
}
yield words;
}
}

var listOfWords: list(list(string));
var str : string = "This, that, the other.";

for words in linesToListsOfStrings(str) {
listOfWords.pushBack(words);
}

writeln(listOfWords, ": ", listOfWords.type:string);

Hi again,

Figured it out. Sorry for the noise.

Best wishes,
Roger

1 Like