require 'test/unit'

  # Author: Reginald Braithwaite
  # http://weblog.raganwald.com/

  class ExempliGratia < Test::Unit::TestCase

    CURRY = lambda { |f, a| lambda { |*b| f.call(a, *b) } }

    def test_recursive_curry
      maker = lambda { |func_with_me|
        CURRY.call(func_with_me, func_with_me)
      }
      fact = maker.call(lambda { |me, n| n.zero? && 1 or n * me.call(me, n-1) })
      assert_equal(120, fact.call(5))
    end

    def test_refactor_fact_to_isolate_desired_form
      maker = lambda { |func_with_me|
        CURRY.call(func_with_me, func_with_me)
      }
      un_fact_ored = lambda { |me, n| n.zero? && 1 or n * me.call(me, n-1) }
      assert_equal(120, maker.call(un_fact_ored).call(5))

      re_fact_ored_1 = lambda { |me, n| n.zero? && 1 or n * CURRY.call(me, me).call(n-1) }
      assert_equal(120, maker.call(re_fact_ored_1).call(5))

      re_fact_ored_2 = lambda { |me, n| (lambda { |better_me| n.zero? && 1 or n * better_me.call(n-1) }).call(CURRY.call(me, me)) }
      assert_equal(120, maker.call(re_fact_ored_2).call(5))

      re_fact_ored_3 = lambda { |me, outer_n|
        (lambda { |better_me| 
          (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }).call(outer_n) }).call(CURRY.call(me, me)) }
      assert_equal(120, maker.call(re_fact_ored_3).call(5))

      re_fact_ored_4 = lambda { |me, outer_n|
        (lambda { |better_me| 
          (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }).call(CURRY.call(me, me)).call(outer_n) }
      assert_equal(120, maker.call(re_fact_ored_4).call(5))
    end

    def test_refactor_maker_to_parameterize_desired_form
      maker_1 = lambda { |func_with_me|
        CURRY.call(func_with_me, func_with_me)
      }
      re_fact_ored_4 = lambda { |me, outer_n|
        (lambda { |better_me| 
          (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }).call(CURRY.call(me, me)).call(outer_n) }
      assert_equal(120, maker_1.call(re_fact_ored_4).call(5))

      re_fact_ored_5 = CURRY.call(lambda { |func, me, outer_n|
        func.call(CURRY.call(me, me)).call(outer_n) }, (lambda { |better_me| 
          (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }))
      assert_equal(120, maker_1.call(re_fact_ored_5).call(5))

      expanded = (lambda { |func_with_me|
        CURRY.call(func_with_me, func_with_me)
      }.call(CURRY.call(lambda { |func, me, outer_n|
          func.call(CURRY.call(me, me)).call(outer_n) }, (lambda { |better_me| 
            (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }))))
      assert_equal(120, expanded.call(5))

      promoted = (lambda { |outer_func|
          (lambda { |func_with_me|
            CURRY.call(func_with_me, func_with_me)
          }.call(CURRY.call(lambda { |func, me, outer_n|
              func.call(CURRY.call(me, me)).call(outer_n) }, outer_func)))
        }).call((lambda { |better_me| 
              (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }))
      assert_equal(120, promoted.call(5))

      #therefore

      maker = (lambda { |outer_func|
          (lambda { |func_with_me|
            CURRY.call(func_with_me, func_with_me)
          }.call(CURRY.call(lambda { |func, me, outer_n|
              func.call(CURRY.call(me, me)).call(outer_n) }, outer_func)))
        })

      assert_equal(120, maker.call((lambda { |better_me| 
              (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) })).call(5))

      extracted_factorial = maker.call((lambda { |better_me| 
                      (lambda { |inner_n| inner_n.zero? && 1 or inner_n * better_me.call(inner_n-1) }) }))
      assert_equal(120, extracted_factorial.call(5))

      renamed_factorial = maker.call((lambda { |f| 
                      (lambda { |n| n.zero? && 1 or n * f.call(n-1) }) }))

      assert_equal(120, renamed_factorial.call(5))

    end

    def test_clean_up_loose_ends
      maker = lambda { |f|
                 lambda { |func_with_me| CURRY.call(func_with_me, func_with_me) }.call(
                      CURRY.call(lambda { |inner_func, me, *args|
                                  inner_func.call(CURRY.call(me, me)).call(*args) }, f)) }

      factorial = maker.call(
        lambda { |f| lambda { |n| n.zero? && 1 or n * f.call(n-1) } }
      )
      assert_equal(120, factorial.call(5))

      iterative_factorial = maker.call(
        lambda { |f| lambda { |n, acc| n.zero? && acc or f.call(n - 1, n * acc) } }
      )
      tail_factorial = lambda { |n| iterative_factorial.call(n, 1) }
      assert_equal(120, tail_factorial.call(5))
    end

  end
Creative Commons License
This work is licensed under a Creative Commons Public Domain License.