Nim: [Offtopic] Functional programming support in the Nim standard library

Created on 14 Jun 2017  路  5Comments  路  Source: nim-lang/Nim

Off topic. Sorry for misusing the issue tracker, but I felt the need for some distraction... Feel free to close if you think this is inappropriate.

I've just stumbled about a small D program I wrote ~2.5 years ago that solves Project Euler problem 30 in one line.

My first tries at translating this into Nim have resulted in some very ugly code (I'm probably missing some powerful functions already available in Nim's standard library), so I'm asking the community for some hints - I'd guess Nim is expressive enough to create a both beautiful and efficient solution.

And finally I wonder if this could be done without any extra memory allocations under the hood, or even at compile time...

Have fun,
Markus

///usr/bin/env true; set -e; exec rdmd "$0" "$@"

// Project Euler problem 30 one-line solutions in D
//   https://projecteuler.net/problem=30
// written by Markus F.X.J. Oberhumer <[email protected]>
// put into the public domain

import std.algorithm : filter, map, sum;
import std.conv : to;
import std.range : iota;
import std.stdio : writeln;

void main()
{
    // classic - nested function calls using lambdas ("=>") as template parameters
    writeln(sum(filter!(a => a == sum(map!(d => (d-'0')^^5)(to!string(a)))) (iota(10, 1_000_000))));

    // using Universal Function Call Syntax (UFCS)
    writeln(iota(10, 1_000_000).filter!(a => a == sum(map!(d => (d-'0')^^5)(to!string(a)))).sum);

    // now the template parameter of filter! also uses UFCS
    writeln(iota(10, 1_000_000).filter!(a => a == a.to!string.map!(d => (d-'0')^^5).sum).sum);

    // now map! uses a "template mixin"
    writeln(iota(10, 1_000_000).filter!(a => a == a.to!string.map!"(a-'0')^^5".sum).sum);

    // now filter! uses a mixin as well (nested string needs backslashes)
    writeln(iota(10, 1_000_000).filter!"a == a.to!string.map!\"(a-'0')^^5\".sum".sum);

    // using a "token string"
    writeln(iota(10, 1_000_000).filter!q{a == a.to!string.map!"(a-'0')^^5".sum}.sum);

    // using a nested "token string"
    writeln(iota(10, 1_000_000).filter!q{a == a.to!string.map!q{(a-'0')^^5}.sum}.sum);
}

Most helpful comment

Your desire to never ever write a "for" loop again seems to have little to do with real programming problems.

All 5 comments

The forum would be the right place for such a question. I suggest to close the issue and move the discussion over there.

If you really want to squeeze that into one line, you can do so in Nim as well:

import future
import sequtils
import math

# I'm introducing a simplified reduce here. The standard
# library has foldl, but that doesn't allow UFCS.
proc reduce[T](s: seq[T], f: (T, T) -> T): T =
  result = T(0)
  for x in s:
    result = f(result, x)

echo toSeq(2..1_000_000).filter(x => x == ($x).map(c => (c.ord - '0'.ord).int ^ 4).reduce((a, b) => a + b)).sum()

Thanks for the feedback. And you are right, I'm closing this issue.

@markus-oberhumer FYI on the latest devel you also don't need the (probably somewhat surprising) type conversion in (c.ord - '0'.ord).int ^ 4 anymore. Previously ord, mod, and bitwise and returned range types, which had to be converted before passing them to ^. Now they return int so it just works.

Note: the D version is lazy (in particular has small memory footprint) ; the version given by @bluenote10 in https://github.com/nim-lang/Nim/issues/5985#issuecomment-308561895 is not, and will allocate lots of memroy

Your desire to never ever write a "for" loop again seems to have little to do with real programming problems.

Was this page helpful?
0 / 5 - 0 ratings