Array access word list
Array access of words in a sentence
Abstract types are very useful for layering strongly-typed functionality over top of more basic classes. They are also the only place where operator overloading (including array access). In this relatively trivial example, we can make use of the @:arrayAccess metadata to allow us to easily access individual words in a sentence (where word boundaries are defined as spaces).
First off, we can describe an abstract over a string, which will define a new type that is 100% a string:
abstract WordList(String) from String to String {
public function new(s:String)
this = s;
}
This is a bit useless on it's own however, so let's add the @:arrayAccess components we're actually after:
abstract WordList(String) from String to String {
public function new(s:String)
this = s;
@:arrayAccess
public inline function getWord(index:Int) {
return this.split(' ')[index];
}
@:arrayAccess
public inline function setWord(index:Int, word:String) {
var words:Array<String> = this.split(' ');
words[index] = word;
return this = words.join(' ');
}
}
These two functions (getWord and setWord) are simple inline functions for extracting or replacing a word in a sentence, and could be called as:
var thirdWord = sentence.getWord(2);
sentence.setWord(1, "moderately-paced");
However, this is a bit verbose. The @:arrayAccess metadata allows us to instead call the functions as:
var thirdWord = sentence[2];
sentence[1] = "moderately-paced";
Usage
var sentence:WordList = "The quick brown fox jumped over the lazy dog's back.";
Sys.println('In the sentence:');
Sys.println(' > ' + sentence);
Sys.println('');
Sys.println('What colour is the fox?');
Sys.println(' > ' + sentence[2]);
Sys.println('');
Sys.println("Let's slow him down...");
sentence[1] = "moderately-paced";
Sys.println(' > ' + sentence);
This will print:
In the sentence:
> The quick brown fox jumped over the lazy dog's back.
What colour is the fox?
> brown
Let's slow him down...
> The moderately-paced brown fox jumped over the lazy dog's back.
Author: Kenton Hamaluik
Doing a string split on each array access looks painful... Shouldn't you define that abstract over Array<String> and do the split only in the constructor?
That was actually the original code:
abstract WordList(Array<String>) {
public function new(words:Array<String>)
this = words;
@:from
public inline static function fromString(s:String)
return new WordList(s.split(' '));
@:to
public inline function toString()
return this.join(' ');
@:arrayAccess
inline function getWord(index:Int) {
return this[index];
}
@:arrayAccess
inline function setWord(index:Int, word:String) {
return this[index] = word;
}
}
But I decided to just abstract it over a string to focus on the @:arrayAccess components. As per #49 I was looking for a relatively simple example showing how to use @:arrayAccess rather than how to do this particular operation most efficiently.
That said, I'd be happy to change it back, or add this version into the article (with a short discussion of why this would be done instead of the first way) if you think that would be more beneficial.
Hi, thanks for the contribution. I agree with @Simn, can you change it so it doesn't split on each access? While I get it's not the point of the article, we should strive to have snippets with good performance.
I would suggest to do something like this:
abstract WordList(Array<String>) {
public function new(word:String, delimiter:String = " ")
this = word.split(delimiter);
Closing due to lack of follow-up.