Skip to content

Instantly share code, notes, and snippets.

@tuscen
Last active November 19, 2015 13:06
Show Gist options
  • Save tuscen/e79adcbf0a0faa1d33c2 to your computer and use it in GitHub Desktop.
Save tuscen/e79adcbf0a0faa1d33c2 to your computer and use it in GitHub Desktop.
Implementation of pairs and lists using closures
const cons = (x, y) => method => {
switch (method) {
case 'car':
return x;
case 'cdr':
return y;
}
}
const car = pair => pair('car');
const cdr = pair => pair('cdr');
const makeList = (...args) => args.reverse().reduce((acc, item) => cons(item, acc), null);
const l = makeList;
// recursive
const append_r = (list1, list2) => {
if (list1 === null) {
return list2;
} else {
return cons(car(list1), append_r(cdr(list1), list2));
}
}
// iterative
const append_i = (list1, list2) => {
const iter = (items, acc) => {
if (items === null) {
return acc;
}
return iter(cdr(items), cons(car(items), acc))
}
return iter(reverse(list1), list2);
}
const length = items => {
if (items === null) {
return 0;
}
return 1 + length(cdr(items));
}
const reverse = list => {
const iter = (items, acc) => {
if (items === null) {
return acc;
} else {
return iter(cdr(items), cons(car(items), acc));
}
};
return iter(list, null);
}
const listRef = (list, n) => {
if (n === 0) {
return car(list);
} else {
return listRef(cdr(list), n - 1);
}
}
const map = (list, func) => {
const iter = (list, acc) => {
if (list === null) {
return reverse(acc);
} else {
return iter(cdr(list), cons(func(car(list)), acc));
}
};
return iter(list, null);
};
const reduce = (list, func, acc) => {
const iter = (list, acc) => {
if (list === null) {
return acc;
} else {
const newAcc = func(car(list), acc);
return iter(cdr(list), newAcc);
}
};
return iter(list, acc);
}
const filter = (list, func) => {
const iter = (list, acc) => {
if (list === null) {
return reverse(acc);
} else {
const newAcc = func(car(list)) ? cons(car(list), acc) : acc;
return iter(cdr(list), newAcc);
}
};
return iter(list, null);
}
const isPair = v => v instanceof Function;
// const listToString = list => {
// const arr = [];
// const iter = items => {
// if (items !== null) {
// arr.push(car(items));
// iter(cdr(items));
// }
// };
// iter(list);
// return "(" + arr.join(", ") + ")";
// }
// Stringify a list using reduce
const listToString = list => {
if (!isPair(list)) return list;
const res = reduce(list, (acc, item) => {
return a.concat(listToString(item))
} , [])
return `(${res.join(", ")})`
}

Pairs

pair = cons(1, 2) # => Proc
pair.call(:car) == car(pair) == 1
pair.call(:cdr) == cdr(pair) == 2

Lists

list = make_list(1, 2, 3, 4, 5) # => Proc
list_to_string(list) # => "(1, 2, 3, 4, 5)"
length(list) # => 5

list2 = make_list(6, 7)
list3 = append(list1, list2) # => Proc
list_to_string(list3) # => "(1, 2, 3, 4, 5, 6, 7)"

list4 = map(list) { |e| e**2 } # => Proc
list_to_string(list4) # => "(1, 4, 9, 16, 25, 36, 49)"

list5 = filter(list) { |e| e.odd? } # => Proc
list_to_string(list5) # => "(1, 3, 5)"

reduce(list, 0) { |a, e| a + e } # => 21

Trees

tree = make_list(1, 2, make_list(3, 4), make_list(5, 6, make_list(7, 8))) # => Proc
list_to_string(tree) # => "(1, 2, (3, 4), (5, 6, (7, 8)))"
  root of a sub-tree
         |
         |  node of a sub-tree
         |  |    
         |  |   root of a sub-tree
         |  |    |
         |  |    |  nodes of a sub-tree
         |  |    |  |  |
         v  v    v  v  v
"(1, 2, (3, 4), (5, 6, (7, 8)))"
  ^  ^  ^       ^
  |  |  |       |
root  \  \     /
       \  \   /
        \  \ /
         nodes 


          (1)
      _____|_____
    /      |      \
  (2)     (3)     (5)
           |      _|_
          (4)   /     \
              (6)     (7)
                       |
                      (8)
def cons(x, y)
lambda do |method|
case method
when :car
x
when :cdr
y
end
end
end
def car pair
pair.call :car
end
def cdr pair
pair.call :cdr
end
def make_list(*args)
args.reverse.reduce(nil) { |a, e| cons(e, a) }
end
def reverse list
iter = lambda do |items, acc|
return acc unless items
iter.call(cdr(items), cons(car(items), acc))
end
iter.call(list, nil)
end
def append(list1, list2)
iter = lambda do |items, acc|
return acc unless items
iter.call(cdr(items), cons(car(items), acc))
end
iter.call(reverse(list1), list2)
end
def length list
iter = lambda do |items, l|
return l unless items
iter.call(cdr(items), l + 1)
end
iter.call(list, 0)
end
def map list
iter = lambda do |items, acc|
return reverse(acc) unless items
new_acc = cons(yield(car(items)), acc)
iter.call(cdr(items), new_acc)
end
iter.call(list, nil)
end
def filter list
iter = lambda do |items, acc|
return reverse(acc) unless items
new_acc = yield(car(items)) ? cons(car(items), acc) : acc
iter.call(cdr(items), new_acc)
end
iter.call(list, nil)
end
def reduce(list, acc)
iter = lambda do |items, acc|
return acc unless items
new_acc = yield(acc, car(items))
iter.call(cdr(items), new_acc)
end
iter.call(list, acc)
end
def pair? v
v.respond_to? :call
end
def list_to_string list
return list unless pair?(list)
res = reduce(list, []) do |a, e|
a << list_to_string(e)
end
"(#{res.join(", ")})"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment