knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.path = "README-"
)

{matchwith}

What is this package?

match_with() simulates pattern matching that is used in many functional programming language such as 'case of' in Haskell and 'match with' in OCaml.

Installation

# install.packages("devtools")
devtools::install_github("tobcap/matchwith")
library("matchwith")

Supports

library(matchwith)

Usage

# Syntax
f <- function(expr) {
  match_with(expr
  , pattern_1 -> res_1
  , pattern_2 -> res_2
             ...
  , pattern_n -> res_n
  )
}

Const pattern

pattern_i represents numeric literal, charater literal, or NULL. When x is matched with pattern_i, res_i (the right side of expression) is evalueted in the parent.frame . In the exampe below, when x is 0 or 1, the right side expression is evalated and returned. when x is more than 1, wildcard simbol is matched and fib(x-1) + fib(x-2) is evaluated and returned.

fib <- function(x)
  match_with(x
  , 0 -> 0
  , 1 -> 1
  , . -> fib(x - 1) + fib(x - 2)
  )
fib(10)

Cons pattern

:: is used for DSL porpose that is representing constructor of head and tail. when pattern is y::ys, the first object of x is assigned to y and rest of that is assigned to ys.

sum1 <- function(x)
  match_with(x
  , integer(0) -> 0L
  , y::ys -> y + sum1(ys)
  )
sum1(1:5)
sum2 <- function(x, acc = 0L)
  match_with(x
  , integer(0) -> acc
  , y::ys -> sum2(ys, acc + y)
  )
sum2(1:5)

Tuple pattern with matching

When pattern_i starts with list and includes new symbols as list's arguments, the coresponding values of x which must have the same language structure will be assigned to the new symbols, and res_i is evaluated with using such symbol-value-associated-list.

ex1 <- function(x)
  match_with(x
  , list(a, b) -> 10 * a + b
  , list(a, b, c) -> 100 * a + 10 * b + c
  , . -> x
  )
ex1(list(1,2))
ex1(list(1,2,3))
str(ex1(list(1,2,3,4)))

Wildcard inside list of pattern_i can be used.

fizzbuzz1 <- function(x)
  match_with(list(x %% 3, x %% 5)
  , list(0, 0) -> "fizzbuzz"
  , list(0, .) -> "fizz"
  , list(., 0) -> "buzz"
  , . -> as.character(x)
  )
sapply(1:30, fizzbuzz1)

Other than list, x is compared with pattern_i and if it matches, res_i is evaluated.

ex2 <- function(x)
  match_with(x
  , 1:3 -> 2 * x
  , 1:5 -> 3 * x
  , . -> 100 * x
  )
ex2(1:3)
ex2(1:5)
ex2(1:4)
ex2(c(1,2,3)) # 1:3 is integer class, c(1,2,3) is numeric class
ex2(c(1L,2L,3L))

Guard clauses

when pattern_i uses Compare family (r getGroupMembers("Compare")) (or !, any, all, identity, isTRUE) or a function whose name starts with is. as a top node of language object, it is regarded as guard clauses, and when the result of evaluating pattern_i is TRUE, res_i will be evaluated and returned.
Note that r getGroupMembers("Logic") possibly return vector of boolean object whose length may have more than 2, so all() or any() is required to use them.

ex3 <- function(x)
  match_with(x
  , x > 5 -> x * 100
  , x < 3 -> x * 10
  , . -> -x
  )
sapply(1:10, ex3)

# thanks to @Keiku issue#1
ex4 <- function(x)
  match_with(x
  , all(1 <= x & x <= 10) -> "1-10"
  , all(11 <= x)          -> "11+"
  , otherwise             -> "other"
  )
ex4(1:10)
ex4(8:12)
ex4(11:14)
ex4(5)
ex4(15)
ex4(-1)


TobCap/matchwith documentation built on May 9, 2019, 4:51 p.m.