# Swift's closure
#   https://d...content-available-to-author-only...e.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html


class String
    class << self
        def __unfoldl_rec__(b, &block)
            return self.to_enum(:__unfoldl_rec__, b) unless block_given?

            if (pair = yield b).nil?
                ''
            else
                a, b1 = pair

                __unfoldl_rec__(b1, &block) + a
            end
        end


        def __unfoldl_until__(b)
            return self.to_enum(:__unfoldl_until__, b) unless block_given?

            s  = ''
            until (pair = yield b).nil?
                a, b = pair

                s += a
            end

            s
        end


        def __unfoldl_loop__(b)
            return self.to_enum(:__unfoldl_loop__, b) unless block_given?

            loop.inject([b, '']) { |(b1, s), _|
                if (pair = yield b1).nil?
                    break s
                else
                    a, b2 = pair

                    [b2, a + s]
                end
            }
        end


        # alias unfoldl __unfoldl_rec__
          alias unfoldl __unfoldl_until__
        # alias unfoldl __unfoldl_loop__
    end
end



DigitNames = {
    0 => "Zero", 1 => "One", 2 => "Two",   3 => "Three", 4 => "Four",
    5 => "Five", 6 => "Six", 7 => "Seven", 8 => "Eight", 9 => "Nine"
}
Numbers = [16, 58, 510]


puts '==== Internal enumerator ===='
strings = Numbers.map { |number|
    String.unfoldl(number) { |num|
        if num <= 0
            nil
        else
            [DigitNames[num % 10], num / 10]
        end
    }
}

for s in strings
    puts s
end


puts
puts '==== External enumerator (aka. Generator) ===='
strings = Numbers.map { |number|
    for num in String.unfoldl(number)
        if num <= 0
            nil
        else
            [DigitNames[num % 10], num / 10]
        end
    end
}

for s in strings
    puts s
end