factors(k) -> for (x : 2 .. k, k % x == 0) -> x;It's not particularly efficient but it is definitely succinct. You can also create infinite data structures:
// factors(18) = [2, 3, 6, 9]
def primes = for (x: Naturals, x > 1, factors(x).is_empty()) -> x;Of course you can also use it for plain, everyday stuff like
// primes = [2, 3, 5, 7, ...]
// primes[100] = 541
def all_names = for (person: employees) -> person.get_name()Compare this with, let's pick a random language, Java:
List all_names = new ArrayList();It's just not the same...
for (Person person: employees)
names.add(person.getName());
Now, how does this actually work? The for expression uses a single method on collections:
map_filter (I'm not too happy about this name so if you have a better idea feel free to tell me). This method takes two block arguments, map and filter and returns a new collection which contains the results of map applied to the elements in this collection for which filter returns true. A straightforward implementation could be:map_filter(map, filter) {
def result = new ArrayList();
for (elm: this) {
if (filter(elm))
result.add(map(elm));
}
return result;
}Note that for statements (for (...) { ... }) are not the same as for expressions (for (...) -> ...).All a for expression is, really, is calls to the
map_filter method. For instance, the body of the factor function above is expanded into(2 .. k).map_filter(fun x -> x, fun x -> k % x == 0)Implementing the
map_filter method should be pretty straightforward and the good thing is that the implementer is free to decide how to represent the result. I definitely like this approach better than requiring collections to implement several different methods. Scala, for instance, uses three: map, filter and flatMap.The freedom to choose the representation of the result is the key to implementing the list of primes: the implementation of
map_filter in Naturals applies the map and filter lazily:class Naturals is Collection {
map_filter(map, filter)
-> new MapFilterCollection(this, map, filter);
operator [index] -> index;
for(block) {
var i = 0;
while (true) {
block(i);
i = i + 1;
}
}
}
class MapFilterCollection is Collection {
def collection, map, filter;
// constructor, etc.
map_filter(map, filter)
-> new MapFilterCollection(this, map, filter);
for(block) {
for (obj: collection) {
if (filter(obj))
block(map(obj));
}
}
operator [index] {
var i = 0;
for (obj : this) {
if (index == i) return obj;
i = i + 1;
}
}
}This is not an efficient implementation of MapFilterCollection but it should give an idea of how things work underneath: the collection just postpones all the work until you iterate through it or ask for a particular element.Apart from the name of the method,
map_filter, I think that's a pretty decent approach. There's still one issue left: what happens when you iterate through several collections at once:for (x: 0 .. 100, y: 0 .. x, z: 0 .. y,This expression uses three
x + y + z == 50) -> new Tuple(x, y, z)
map_filters, one for each variable, but the result has to be turned into a single collection. I have implemented a solution but I think is still needs a bit of work. I'll write more about that later.

0 comments:
Post a Comment