Exemple #1
0
def test_iter():
    one_to_ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    # f object support iteration on it's output
    func = f(map) << 1 .__add__ << range(10) | list
    assert func.value == one_to_ten
    # func is an f object, not a list
    assert type(func) == f != list

    # f object is iterable
    res = []
    for n in func:
        res.append(n)
    assert res == one_to_ten

    # works in list comprehension
    res = [n for n in func]
    assert res == one_to_ten

    # and as arguments for functions that expect an iterator
    res = list(func)
    assert res == one_to_ten

    # works with functions return a single non-iterable value, too
    # f object will trun it into a 1-tuple
    answer = f(42)
    res = []
    for n in answer:
        res.append(n)
    assert res == [42]

    res = [n for n in answer]
    assert res == [42]

    res = list(answer)
    assert res == [42]
Exemple #2
0
def test_as_identity():
    # f works as identity function on constant (non-callable)
    assert f(1) == 1
    assert f(2) != 1

    # f wraps functions and works as original functions
    assert f(sum(range(10))) == f(sum)(range(10)) == sum(f(range)(10)) == 45
Exemple #3
0
def test_reverse_apply():
    minus = f(lambda a, b: a - b)
    assert minus(2, 1) == 1
    assert minus.reverse_apply()(2, 1) == -1

    # support more than 2 arguments
    make_list = f(lambda *args: list(args))
    make_reversed_list = make_list.reverse_apply()
    assert make_list(1, 2, 3, 4, 5) == [1, 2, 3, 4, 5]
    assert make_reversed_list(1, 2, 3, 4, 5) == [5, 4, 3, 2, 1]
Exemple #4
0
def test_operator_pipe():
    filter_ = f(filter)
    map_ = f(map)

    odd = lambda n: n % 2 == 1
    snd = lambda e: e[1]

    # as the name implies, it works like pipe in POSIX shell
    odd_num = range(10) | filter_ << odd | enumerate | map_ << snd | list
    assert odd_num == [1, 3, 5, 7, 9]
    # pipe is equivalent to function composition in reversed order
    fc = list ** (map_ << snd) ** enumerate ** (filter_ << odd) ** range(10)
    assert fc == odd_num
Exemple #5
0
def test_auto_eval():
    # equality test on invokes function wrap by f object
    # this is done by overloading operator == and != of f object
    assert f(sum(range(1, 11))) == 55

    # works in both directions
    assert 55 == f(sum(range(1, 11)))

    # even with f objects on both sides
    assert f(sum(range(1, 11))) == f(sum(range(1, 11)))

    g = f(sum(range(1, 101)))
    assert g == g.value == g() == g.call() == g.invoke() == 5050
Exemple #6
0
def test_operator_compose():
    # make some curried functions
    add_2 = add << 2
    mul_5 = mul << 5

    # and make sure they work
    assert add_2(4) == 6
    assert mul_5(3) == 15

    # operator ** can be used for function composition
    add_2_mul_5 = add_2 ** mul_5
    assert add_2_mul_5(1) == 7
    assert add_2_mul_5(2) == 12
    assert add_2_mul_5(3) == 17

    # pay attention to the order of evaluation, operator ** is
    # right-associative, this is in accordance with the function composition
    # operator in haskell, the dot operator (.)
    mul_5_add_2 = mul_5 ** add_2
    assert mul_5_add_2(1) == 15
    assert mul_5_add_2(2) == 20
    assert mul_5_add_2(3) == 25

    # one more example of right-associative
    assert neg ** abs << -1 == -1

    # function composition in action
    from itertools import count, takewhile as tw
    takewhile = f(tw)
    lt_20 = lambda n: n < 20
    reverse = lambda s: s[::-1]
    r = [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    assert reverse ** list ** (takewhile << lt_20) ** count == r

    # result of function composition will alway be f object, which means, in a
    # chain of function composition, we can have as few as a well-placed
    # single f object to make the expression valid.  in order words, both
    # __pow__ and __rpow__ are implemented.
    assert f(list) ** f(map) << (add << 1) << range(10) == list(range(1, 11))
    assert list ** f(map) << (add << 1) << range(10) == list(range(1, 11))

    # to see it more clearly with identity function
    identity = lambda x: x
    # __pow__ in action
    assert f(identity) ** 42 == 42
    # __rpow__ in action
    assert identity ** f(42) == 42
    assert identity ** identity ** f(42) == 42
    assert identity ** identity ** identity ** f(42) == 42
    assert identity ** identity ** identity ** identity ** f(42) == 42
Exemple #7
0
def test_contains():
    # for function with single value
    f_always_true = f(True)

    # works like equality check
    assert True in f_always_true
    assert False not in f_always_true

    # for function with sequence as value
    one_to_ten = range(1, 11)
    list_of_one_to_ten = f(list) << one_to_ten
    # performs membership test on value
    for i in one_to_ten:
        assert i in list_of_one_to_ten
    # make sure it did check membership instead of return True blindly
    minus_one_to_ten = range(-1, -11, -1)
    for i in minus_one_to_ten:
        assert i not in list_of_one_to_ten
Exemple #8
0
def test_clone():
    # clone should always return a new instance.
    the_answer = f(42)
    assert the_answer is the_answer
    assert the_answer is not the_answer.clone(the_answer)
    assert the_answer is not the_answer.clone(42)
    assert the_answer.clone(the_answer) is not the_answer.clone(42)
    assert the_answer.clone(the_answer) is not the_answer.clone(the_answer)
    assert the_answer.clone(42) is not the_answer.clone(42)
    assert the_answer is the_answer
Exemple #9
0
def test_flip():
    # f.flip, as function flip in haskell
    minus = f(lambda a, b: a - b)
    assert minus << 3 << 2 == 1

    two_minus = minus << 2
    assert two_minus << 1 == 1

    minus_two = minus.flip << 2
    assert minus_two << 1 == -1

    # flip twice yield the same function (sort of, there might be overhead
    # added in reality)
    assert minus(2, 1) == minus.flip.flip(2, 1)
Exemple #10
0
def test_constructor():
    # works with lambda expressions
    assert f(lambda: 42) == 42
    assert f(lambda a, b: a + b)(5, 6) == 11

    # works with buildin functions
    assert f(len)([1, 1, 2, 3, 5]) == 5
    assert f(min)(3, 2, 1) == 1

    # works with type constructors
    assert f(set)() == set()
    assert f(list)(range(3)) == [0, 1, 2]
Exemple #11
0
def test_pipe():
    one = f(1)

    res = one.pipe(add << 2).pipe(mul << 5).pipe(sub.flip << 40).pipe(neg)
    # -(40 - (1 + 2) * 5) == 25
    assert res == 25
    # equivalent operator pipe form
    res = one | add << 2 | mul << 5 | sub.flip << 40 | neg
    assert res == 25

    g = (add << 2).pipe(mul << 5).pipe(sub.flip << 40).pipe(neg)
    res = one.pipe(g)
    assert res == 25

    # equivalent operator pipe form
    g = add << 2 | mul << 5 | sub.flip << 40 | neg
    res = one | g
    assert res == 25
Exemple #12
0
def test_apply():
    # applying arguments to f object returns partial applied functions.
    # when apply one by one, it's called currying.
    assert add.apply(1).apply(2)() == 3
    assert sub.apply(1).apply(2)() == -1

    # apply can accept more than one arguments at a time
    add_1_4 = add.apply(1, 4)
    assert add_1_4 == 5

    # supports keyword arguments as well, notice named argument 'base' is
    # supposed to be the second argument of int
    from_hex = f(int).apply(base=16)
    assert from_hex('0a') == 10
    assert from_hex('0b') == 11
    assert from_hex('0c') == 12
    assert from_hex('0d') == 13
    assert from_hex('0e') == 14
    assert from_hex('0f') == 15
    assert from_hex('10') == 16
Exemple #13
0
def test_operator_high_cohesive_invoke():
    # f objects can be called as functions
    assert add(1, 2) == 3
    assert sub(7, 3) == 4
    assert mul(3, 7) == 21
    assert truediv(32, 4) == 8
    assert neg(1) == -1

    # works with arbitrary numbers of arguments
    assert f(bool)() is False
    assert f(max)([1]) == 1
    assert f(max)(6, 1) == 6
    assert f(max)(6, 1, 9) == 9
    assert f(max)(*range(10)) == 9

    # works with keyword arguments
    assert f(int)('10', base=16) == 16
Exemple #14
0
def test_operator_high_cohesive_apply():
    # operator << on f object is overloaded, works as f.apply
    assert (add << 1 << 2) == 3
    assert (sub << 1 << 2) == -1

    # because of operator == has lower precedence than operator <<,
    # parentheses around f object can be omitted.
    assert add << 1 << 2 == 3
    assert sub << 1 << 2 == -1

    # add takes two arguments
    assert add(1, 2) == 3
    # apply one argument to it, make it a curried function that takes one
    # argument
    add_1 = add << 1
    # invokes it with one argument yields result.
    assert add_1(1) == 2
    assert add_1(2) == 3
    assert add_1(3) == 4
    # curry again, becomes a function takes no argument
    add_1_to_4 = add_1 << 4
    assert add_1_to_4() == 5
    # do it all in one expression
    assert (add << 1 << 4)() == 5

    # chaining apply
    res = list(str(n) for n in range(10))
    assert f(map) << str << range(10) | list == res

    # augmented assignments supported, too
    fm = f(map)
    fm <<= str
    fm <<= range(10)
    assert list(fm.value) == res

    # combine with other operators
    list_of_numbers = ~f(map) << range(5) | list
    add_1 = f(1 .__add__)
    assert list_of_numbers << add_1 == [1, 2, 3, 4, 5]
    times_2 = f(2 .__mul__)
    assert list_of_numbers << times_2 == [0, 2, 4, 6, 8]
    divided_by_2 = ~f(truediv) << 2
    assert list_of_numbers << divided_by_2 == [0, 0.5, 1, 1.5, 2]
Exemple #15
0
from fx.function import Function as f

from functools import partial
from operator import add, sub, mul, truediv, neg
# wrap them all with f objects
add, sub, mul, truediv, neg = f(add), f(sub), f(mul), f(truediv), f(neg)


def test_constructor():
    # works with lambda expressions
    assert f(lambda: 42) == 42
    assert f(lambda a, b: a + b)(5, 6) == 11

    # works with buildin functions
    assert f(len)([1, 1, 2, 3, 5]) == 5
    assert f(min)(3, 2, 1) == 1

    # works with type constructors
    assert f(set)() == set()
    assert f(list)(range(3)) == [0, 1, 2]


def test_clone():
    # clone should always return a new instance.
    the_answer = f(42)
    assert the_answer is the_answer
    assert the_answer is not the_answer.clone(the_answer)
    assert the_answer is not the_answer.clone(42)
    assert the_answer.clone(the_answer) is not the_answer.clone(42)
    assert the_answer.clone(the_answer) is not the_answer.clone(the_answer)
    assert the_answer.clone(42) is not the_answer.clone(42)
Exemple #16
0
def test_operator_flip():
    minus = f(lambda a, b: a - b)

    # operator ~ works as f.flip
    minus_two = ~minus << 2
    assert minus_two << 1 == -1
Exemple #17
0
def test_operator_low_cohesive_apply():
    # when using higher-order functions, parentheses might be needed
    assert f(map) << (add << 1) << range(5) | list == [1, 2, 3, 4, 5]
    # with low cohesive apply operator, parentheses can be omitted
    assert f(map) & add << 1 & range(5) | list == [1, 2, 3, 4, 5]
Exemple #18
0
def test_partial_compatible():
    # f object works with partial functions (created by functools.partial)
    add = lambda a, b: a + b
    add_1 = partial(add, 1)
    assert add_1(2) == 3
    assert f(add_1) << 2 == 3