Module sequtils

Author: Alex Mitchell

This module implements operations for the built-in seq type which were inspired by functional programming languages. If you are looking for the typical map function which applies a function to every element in a sequence, it already exists in the system module in both mutable and immutable styles.

Also, for functional style programming you may want to pass anonymous procs to procs like filter to reduce typing. Anonymous procs can use the special do notation which is more convenient in certain situations.

Note: This interface will change as soon as the compiler supports closures and proper coroutines.

Procs

proc concat[T](seqs: varargs[seq[T]]): seq[T]

Takes several sequences' items and returns them inside a new sequence.

Example:

let
  s1 = @[1, 2, 3]
  s2 = @[4, 5]
  s3 = @[6, 7]
  total = concat(s1, s2, s3)
assert total == @[1, 2, 3, 4, 5, 6, 7]
proc distnct[T](seq1: seq[T]): seq[T]

Returns a new sequence without duplicates.

This proc is misspelled on purpose to avoid a clash with the keyword distinct used to define a derived type incompatible with its base type. Example:

let
  dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
  dup2 = @["a", "a", "c", "d", "d"]
  unique1 = distnct(dup1)
  unique2 = distnct(dup2)
assert unique1 == @[1, 3, 4, 2, 8]
assert unique2 == @["a", "c", "d"]
proc zip[S, T](seq1: seq[S]; seq2: seq[T]): seq[tuple[a: S, b: T]]

Returns a new sequence with a combination of the two input sequences.

For convenience you can access the returned tuples through the named fields a and b. If one sequence is shorter, the remaining items in the longer sequence are discarded. Example:

let
  short = @[1, 2, 3]
  long = @[6, 5, 4, 3, 2, 1]
  words = @["one", "two", "three"]
  zip1 = zip(short, long)
  zip2 = zip(short, words)
assert zip1 == @[(1, 6), (2, 5), (3, 4)]
assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
assert zip1[2].b == 4
assert zip2[2].b == "three"
proc filter[T](seq1: seq[T]; pred: proc (item: T): bool {.closure.}): seq[T]

Returns a new sequence with all the items that fulfilled the predicate.

Example:

let
  colors = @["red", "yellow", "black"]
  f1 = filter(colors, proc(x: string): bool = x.len < 6)
  f2 = filter(colors) do (x: string) -> bool : x.len > 5
assert f1 == @["red", "black"]
assert f2 == @["yellow"]
proc delete[T](s: var seq[T]; first = 0; last = 0)

Deletes in s the items at position first .. last. This modifies s itself, it does not return a copy.

Example:

let outcome = @[1,1,1,1,1,1,1,1]
var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.delete(3, 8)
assert outcome == dest
proc insert[T](dest: var seq[T]; src: openArray[T]; pos = 0)

Inserts items from src into dest at position pos. This modifies dest itself, it does not return a copy.

Example:

var dest = @[1,1,1,1,1,1,1,1]
let
  src = @[2,2,2,2,2,2]
  outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.insert(src, 3)
assert dest == outcome

Iterators

iterator filter[T](seq1: seq[T]; pred: proc (item: T): bool {.closure.}): T

Iterates through a sequence and yields every item that fulfills the predicate.

Example:

let numbers = @[1, 4, 5, 8, 9, 7, 4]
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
  echo($n)
# echoes 4, 8, 4 in separate lines

Templates

template filterIt(seq1, pred: expr): expr {.immediate.}

Returns a new sequence with all the items that fulfilled the predicate.

Unlike the proc version, the predicate needs to be an expression using the it variable for testing, like: filterIt("abcxyz", it == 'x'). Example:

let
  temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
  acceptable = filterIt(temperatures, it < 50 and it > -10)
  notAcceptable = filterIt(temperatures, it > 50 or it < -10)
assert acceptable == @[-2.0, 24.5, 44.31]
assert notAcceptable == @[-272.15, 99.9, -113.44]
template toSeq(iter: expr): expr {.immediate.}

Transforms any iterator into a sequence.

Example:

let
  numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
  odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
    if x mod 2 == 1:
      result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]
template foldl(sequence, operation: expr): expr

Template to fold a sequence from left to right, returning the accumulation.

The sequence is required to have at least a single element. Debug versions of your program will assert in this situation but release versions will happily go ahead. If the sequence has a single element it will be returned without applying operation.

The operation parameter should be an expression which uses the variables a and b for each step of the fold. Since this is a left fold, for non associative binary operations like substraction think that the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - 3). Example:

let
  numbers = @[5, 9, 11]
  addition = foldl(numbers, a + b)
  substraction = foldl(numbers, a - b)
  multiplication = foldl(numbers, a * b)
  words = @["nim", "rod", "is", "cool"]
  concatenation = foldl(words, a & b)
assert addition == 25, "Addition is (((5)+9)+11)"
assert substraction == -15, "Substraction is (((5)-9)-11)"
assert multiplication == 495, "Multiplication is (((5)*9)*11)"
assert concatenation == "nimrodiscool"
template foldr(sequence, operation: expr): expr

Template to fold a sequence from right to left, returning the accumulation.

The sequence is required to have at least a single element. Debug versions of your program will assert in this situation but release versions will happily go ahead. If the sequence has a single element it will be returned without applying operation.

The operation parameter should be an expression which uses the variables a and b for each step of the fold. Since this is a right fold, for non associative binary operations like substraction think that the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - (3))). Example:

let
  numbers = @[5, 9, 11]
  addition = foldr(numbers, a + b)
  substraction = foldr(numbers, a - b)
  multiplication = foldr(numbers, a * b)
  words = @["nim", "rod", "is", "cool"]
  concatenation = foldr(words, a & b)
assert addition == 25, "Addition is (5+(9+(11)))"
assert substraction == 7, "Substraction is (5-(9-(11)))"
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimrodiscool"
template mapIt(seq1, typ, pred: expr): expr

Convenience template around the map proc to reduce typing.

The template injects the it variable which you can use directly in an expression. You also need to pass as typ the type of the expression, since the new returned sequence can have a different type than the original. Example:

let
  nums = @[1, 2, 3, 4]
  strings = nums.mapIt(string, $(4 * it))
template mapIt(varSeq, pred: expr)

Convenience template around the mutable map proc to reduce typing.

The template injects the it variable which you can use directly in an expression. The expression has to return the same type as the sequence you are mutating. Example:

var nums = @[1, 2, 3, 4]
nums.mapIt(it * 3)
assert nums[0] + nums[3] == 15
Generated: 2014-03-11 21:26:51 UTC