We have talked about allowing the syntax
max(f(x) for x in itr)
as a shorthand for producing each of the values f(x)
in one coroutine while computing the max in another coroutine. This would basically be shorthand for something like this:
max(@task for x in itr; produce(f(x)); end)
Note, however, that this syntax that explicitly creates a task already works, although it is somewhat less pretty than the above. Your problem can be expressed like this:
max(@task for x=1:N, y=x:N
string(x*y) == reverse(string(x*y)) && produce(x*y)
end)
With the hypothetical producer syntax above, it could be reduced to something like this:
max(x*y if string(x*y) == reverse(string(x*y) for x=1:N, y=x:N)
While I'm a fan of functional style, in this case I would probably just use a for loop:
m = 0
for x = 1:N, y = x:N
n = x*y
string(n) == reverse(string(n)) || continue
m < n && (m = n)
end
Personally, I don't find this version much harder to read and it will certainly be quite fast in Julia. In general, while functional style can be convenient and pretty, if your primary focus is on performance, then explicit for loops are your friend. Nevertheless, we should make sure that John's max/filter/product version works. The for loop version also makes other optimizations easier to add, like Harlan's suggestion of reversing the loop ordering and exiting on the first palindrome you find. There are also faster ways to check if a number is a palindrome in a given base than actually creating and comparing strings.
As to the general question of "getting flexible generators and list comprehensions in Julia", the language already has
- A general high-performance iteration protocol based on the start/done/next functions.
- Far more powerful multidimensional array comprehensions than most languages. At this point, the only missing feature is the
if
guard, which is complicated by the interaction with multidimensional comprehensions and the need to potentially dynamically grow the resulting array.
- Coroutines (aka tasks) which allow, among other patterns, the producer-consumer pattern.
Python has the if
guard but doesn't worry about comprehension performance nearly as much – if we're going to add that feature to Julia's comprehensions, we're going to do it in a way that's both fast and interacts well with multidimensional arrays, hence the delay.
Update: The max
function is now called maximum
(maximum
is to max
as sum
is to +
) and the generator syntax and/or filters work on master, so for example, you can do this:
julia> @time maximum(100x - x^2 for x = 1:100 if x % 3 == 0)
0.059185 seconds (31.16 k allocations: 1.307 MB)
2499
Once 0.5 is out, I'll update this answer more thoroughly.
filter(isPalindrome, ...)
here. – StefanKarpinski