summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorHans Raaf <hara@oderwat.de>2016-03-05 16:45:32 +0100
committerHans Raaf <hara@oderwat.de>2016-03-06 17:52:18 +0100
commitcddabcfc63b0643a5c5ba0d065b5f502263dec78 (patch)
tree200cd3b5db2a5e5d5f894d671dca82f400c8b3d3
parente4e8ebd4a50bc86b0b08df449d5ff72e365a4c6a (diff)
downloadNim-cddabcfc63b0643a5c5ba0d065b5f502263dec78.tar.gz
Fixes foldl() and foldr() + foldl() with start parameter.
This fixes the (potential) multi-evaluation of the sequence parameter in
foldl() and foldr().

It also adds a foldl() version which gets a start parameter. This allows
for creating a result with a different type than the elements of the
sequence.
-rw-r--r--lib/pure/collections/sequtils.nim47
1 files changed, 37 insertions, 10 deletions
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index b72face91..0e3824a81 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -508,13 +508,39 @@ template foldl*(sequence, operation: expr): expr =
   ##   assert subtraction == -15, "Subtraction is (((5)-9)-11)"
   ##   assert multiplication == 495, "Multiplication is (((5)*9)*11)"
   ##   assert concatenation == "nimiscool"
-  assert sequence.len > 0, "Can't fold empty sequences"
-  var result {.gensym.}: type(sequence[0])
-  result = sequence[0]
-  for i in 1..<sequence.len:
+  let s = sequence
+  assert s.len > 0, "Can't fold empty sequences"
+  var result {.gensym.}: type(s[0])
+  result = s[0]
+  for i in 1..<s.len:
     let
       a {.inject.} = result
-      b {.inject.} = sequence[i]
+      b {.inject.} = s[i]
+    result = operation
+  result
+
+template foldl*(sequence, operation: expr, first): expr =
+  ## Template to fold a sequence from left to right, returning the accumulation.
+  ##
+  ## This version of ``foldl`` gets a starting parameter. This makes it possible
+  ## to accumulate the sequence into a different type than the sequence elements.
+  ##
+  ## The ``operation`` parameter should be an expression which uses the variables
+  ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the
+  ## start value (the first ``a``) and therefor defines the type of the result.
+  ## Example:
+  ##
+  ## .. code-block::
+  ##   let
+  ##     numbers = @[0, 8, 1, 5]
+  ##     digits = foldl(numbers, a & (chr(b + ord('0'))), "")
+  ##   assert digits == "0815"
+  var result {.gensym.}: type(first)
+  result = first
+  for x in items(sequence):
+    let
+      a {.inject.} = result
+      b {.inject.} = x
     result = operation
   result
 
@@ -544,12 +570,13 @@ template foldr*(sequence, operation: expr): expr =
   ##   assert subtraction == 7, "Subtraction is (5-(9-(11)))"
   ##   assert multiplication == 495, "Multiplication is (5*(9*(11)))"
   ##   assert concatenation == "nimiscool"
-  assert sequence.len > 0, "Can't fold empty sequences"
-  var result {.gensym.}: type(sequence[0])
-  result = sequence[sequence.len - 1]
-  for i in countdown(sequence.len - 2, 0):
+  let s = sequence
+  assert s.len > 0, "Can't fold empty sequences"
+  var result {.gensym.}: type(s[0])
+  result = sequence[s.len - 1]
+  for i in countdown(s.len - 2, 0):
     let
-      a {.inject.} = sequence[i]
+      a {.inject.} = s[i]
       b {.inject.} = result
     result = operation
   result