[QUIZ] Symbolify (#169)

i've got one line - tests pass.

a @ http://codeforpeople.com/

···

On Jul 11, 2008, at 9:17 AM, Matthew Moss wrote:

That is, the following test code should raise no exceptions:

   1000.times do |i|
     s = symbolify(i)
     raise "Not a string!" unless s.is_a? String
     raise "Invalid chars!" unless s.delete("?*()-").empty?

     x = eval(s)
     raise "Decode failed!" unless i == x
   end

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Excellent quiz. Full marks Matthew. :wink:

James Edward Gray II

···

On Jul 11, 2008, at 10:17 AM, Matthew Moss wrote:

## Symbolify (#169)

Your task this week is to count. Yup, that's it.

Oh, by the way... You may only use the characters `?`, `*`, `(`, `)` and `-`.

This is not required extra work... just for a little more fun. I have
some suspicions about some techniques, what I call "ugly" cheats (as
opposed to the "pretty" cheats who ignored you in high school... umm,
ignore that last part).

Anyway, here's another test:

  nums = (0...1000).sort_by { rand }
  strs = nums.map { |n| symbolify(n) }

  strs.zip(nums).sort_by { rand }.each do |str, num|
    res = eval(str)
    raise "Not a string!" unless str.is_a? String
    raise "Invalid chars!" unless str.delete("?*()-").empty?
    raise "Decode failed!" unless res == num
  end

  puts "Passed!"

This might affect some, maybe not others. I wrote two solutions, one
classified as "pretty" while the other is "ugly" (i.e. fails this
test). Again, you're not required to pass this... Looking at some ugly
solutions will certainly be okay.

Interestingly, my pretty solution confuses IRB. If I run on the
command line:
   > ruby -r moss test.rb
   Passed!

However, if I start up irb:
   > require 'moss'
   => true
   > symbolify(60)

Then I get a nice stack crawl (not gonna show yet, spoiler) that ends
in "Maybe IRB bug!!!". (I imagine you guys can guess what I'm
doing...)

eval(symbolify(1200))
p symbolify(999).length
p symbolify(9999).length
p symbolify(999999).length
p symbolify(12345678901234567890).length
p symbolify(("9"*2100).to_i).length
nums = (0...1000).sort_by { rand }
strs = nums.map { |n| symbolify(n) }
strs.zip(nums).sort_by { rand }.each do |str, num|
  res = eval(str)
  raise "Not a string!" unless str.is_a? String
  raise "Invalid chars!" unless str.delete("?*()-").empty?
  raise "Decode failed!" unless res == num
end
puts "Passed!"

outputs:
3
4
6
20
2100
Passed!

Now I'm going for the no cheating.

Lucas

···

On Jul 11, 12:17 pm, Matthew Moss <matthew.m...@gmail.com> wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
## Symbolify (#169)

23 characters symbolify(i) body, no cheating.

···

On Jul 11, 2008, at 8:17 AM, Matthew Moss wrote:

   1000.times do |i|
     s = symbolify(i)
     raise "Not a string!" unless s.is_a? String
     raise "Invalid chars!" unless s.delete("?*()-").empty?

     x = eval(s)
     raise "Decode failed!" unless i == x
   end

My golfed solutions (with whitespace for readability):

def symbolify_1(i)
    # natural numbers only
    "??-??" + "-?(--?)" * i
end

def symbolify_2(i)
    # all integers
    "??-??" + (i<0 ? "-?)--?(" : "-?(--?)") * i.abs
end

-A

···

On Fri, Jul 11, 2008 at 11:17 AM, Matthew Moss <matthew.moss@gmail.com> wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

    <http://splatbang.com/rubyquiz/&gt;\.

3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Symbolify (#169)

Your task this week is to count. Yup, that's it.

Oh, by the way... You may only use the characters `?`, `*`, `(`, `)` and `-`.

Specifically, define a function `symbolify` that accepts an integer
and returns a string composed of only the five characters shown above.
The string, when evaluated, will equal the original number passed in.

That is, the following test code should raise no exceptions:

   1000.times do |i|
     s = symbolify(i)
     raise "Not a string!" unless s.is_a? String
     raise "Invalid chars!" unless s.delete("?*()-").empty?

     x = eval(s)
     raise "Decode failed!" unless i == x
   end

There are at least a few different approaches that come to mind, so
don't expect everyone to have the same output. Well, not before the
output is passed through `eval`, that is.

I am not requiring that you produce the shortest string (though you
are welcome to attempt such a thing). The only thing I _do_ ask is
that you not produce megabytes of data to represent a simple integer.

P.S. Cheating is encouraged (except that you may not write code
outside of `symbolify`).

--
Matthew Moss <matthew.moss@gmail.com>

My first version cheats a bit, but passes the tests. Converts the num
to base-5 and back.

def symbolify(num)
  def eval(str)
    str.tr('(?*)-', '01234').to_i(5)
  end
  num.to_s(5).tr('01234', '(?*)-')
end

My second solution cheats badly, failing the delayed eval test. Still,
it's a single line of length 42. :slight_smile:

def symbolify(num)
  def eval(str) $num end; $num = num; "()"
end

I need to finish my non-cheating version...

Here's my long and expensive search:

module Symbolify

  #allowed characters: ? * ( ) -

  PRIMITIVES = { 40 => "?(", 41 => "?)", 42 => "?*", 45 => "?-", 63 =>
"??" }
  OPERATIONS = [:add, :subtract, :multiply, :exponentiate]

  def symbolify target
    @cache ||= Hash.new

    result = search PRIMITIVES, PRIMITIVES, target
    return result if result

    result = search cache_copy, PRIMITIVES, target
    return result if result

    result = search PRIMITIVES, cache_copy, target
    return result if result

    result = search cache_copy, cache_copy, target
    return result if result

    nil
  end

  private

  def cache_copy
    @cache.dup
  end

  def search alpha_space, beta_space, target
    alpha_space.each do |(value_of_a, a)|
      beta_space.each do |(value_of_b, b)|
        expression = evaluate(a, b, target)
        return expression if expression
      end
    end

    nil
  end

  def evaluate a, b, target
    OPERATIONS.each do |op|
      expression = send(op, a, b)
      next unless expression

      value = eval(expression)
      @cache[value] ||= expression if value >= 0 and value <= 1000

      return expression if value == target
    end

    nil
  end

  def add a, b
    "(#{a}--#{b})"
  end

  def subtract a, b
    return nil unless eval(a) >= eval(b)
    "(#{a}-#{b})"
  end

  def multiply a, b
    "(#{a}*#{b})"
  end

  def exponentiate a, n
    return nil unless eval(n) <= 10
    "(#{a}**#{n})"
  end

end

···

On Jul 11, 11:17 am, "Matthew Moss" <matthew.m...@gmail.com> wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/&gt;\.

3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Symbolify (#169)

Your task this week is to count. Yup, that's it.

Oh, by the way... You may only use the characters `?`, `*`, `(`, `)` and `-`.

Specifically, define a function `symbolify` that accepts an integer
and returns a string composed of only the five characters shown above.
The string, when evaluated, will equal the original number passed in.

That is, the following test code should raise no exceptions:

    1000.times do |i|
      s = symbolify(i)
      raise "Not a string!" unless s.is_a? String
      raise "Invalid chars!" unless s.delete("?*()-").empty?

      x = eval(s)
      raise "Decode failed!" unless i == x
    end

There are at least a few different approaches that come to mind, so
don't expect everyone to have the same output. Well, not before the
output is passed through `eval`, that is.

I am not requiring that you produce the shortest string (though you
are welcome to attempt such a thing). The only thing I _do_ ask is
that you not produce megabytes of data to represent a simple integer.

P.S. Cheating is encouraged (except that you may not write code
outside of `symbolify`).

--
Matthew Moss <matthew.m...@gmail.com>

After sleeping on it, I'm down to 19 chars, no cheating:

def symbolify(i)
"?*-?*"+"-?)--?*"*i
end

    1000.times do |i|
      s = symbolify(i)
      raise "Not a string!" unless s.is_a? String
      raise "Invalid chars!" unless s.delete("?*()-").empty?

      x = eval(s)
      raise "Decode failed!" unless i == x
    end

    puts "Passed!"

My solution:

def symbolify(i)
("?)-?(--"*i)[0..-3]
end

However, at about 3300 I get a SystemStackError and if I start at like 5000 I
segfault the ruby interpreter (1.8.6) :wink:

martin

···

On Friday 11 July 2008 16:17:15 Matthew Moss wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/&gt;\.

3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Symbolify (#169)

Your task this week is to count. Yup, that's it.

Oh, by the way... You may only use the characters `?`, `*`, `(`, `)` and
`-`.

Specifically, define a function `symbolify` that accepts an integer
and returns a string composed of only the five characters shown above.
The string, when evaluated, will equal the original number passed in.

That is, the following test code should raise no exceptions:

    1000.times do |i|
      s = symbolify(i)
      raise "Not a string!" unless s.is_a? String
      raise "Invalid chars!" unless s.delete("?*()-").empty?

      x = eval(s)
      raise "Decode failed!" unless i == x
    end

There are at least a few different approaches that come to mind, so
don't expect everyone to have the same output. Well, not before the
output is passed through `eval`, that is.

I am not requiring that you produce the shortest string (though you
are welcome to attempt such a thing). The only thing I _do_ ask is
that you not produce megabytes of data to represent a simple integer.

P.S. Cheating is encouraged (except that you may not write code
outside of `symbolify`).

I thought by cheating you meant duck typing, not rewriting eval:

def symbolify (n)
  s = "#{n}"
  def s.delete(*args)
    ""
  end
  s
end

Lucas.

···

On Jul 11, 12:17 pm, Matthew Moss <matthew.m...@gmail.com> wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
## Symbolify (#169)

Enjoyed the quiz - thanks! Here are my solutions...

# First, the CHEAT versions.

# The first is an 18-character Kobayashi Maru cheat,
# i.e., following the teachings of Adm. James T Kirk,
# "I reprogrammed the simulation... I don't like to lose."

def symbolify(i)
  def raise(a)end;""
end

# Next, the 14-character uber-cheeze... (Yes, it # outputs the success indicator on stderr instead
# of stdout, ... but who's going to notice? :slight_smile:

def symbolify(i)
  abort"Passed!"
end

# Finally, the NON-CHEAT versions.
# Nothing special but here's what I came up with:

# Default version. Constructs a binary number, # multiplying the previous digit by 2 before adding
# in the next digit...

def symbolify(i)
  x="(?(-?()";i.to_s(2).each_byte{|d|x="(#{x}*(?*-?()--(?#{(d-8).chr}-?())"};x
end

# Sans-multiply solution. (Does not use '*')
# Constructs a binary number,
# and performs each left shift by adding the previous
# value to itself. Generates VERY large strings. Passes
# the tests, but needs to be run with `ulimit -s unlimited`
# :slight_smile:
def symbolify(i)
  x="(?(-?()";i.to_s(2).each_byte{|d|x+="--#{x}--(?#{(d-8).chr}-?()"};x
end

Regards,

Bill

Here is my solution. It's not really outstanding on any front, but it works and it's my first Ruby Quiz submission.

def symbolify(i)
   i.zero?? "??-??":"-"+("(?(-?))-"*i).chop
end

Also, I added this part for fun:

require 'mathn'
def symbolify_short(i)
   return "??-??" if i.zero?
   factors = i.prime_division
   factors.inject("") do |string,pair|
     string << "(" << symbolify(pair.first) << ")**(" << symbolify(pair.last) << ")*"
   end.chop
end

-Dana

···

--
Dana Merrick - System Administrator
Integrated Computer Solutions, Inc.
54B Middlesex Tpke, Bedford, MA 01730
617.621.0060 x112 - http://www.ics.com

a full-on cheat:

cfp:~/src/ruby > cat a.rb
# impl

···

On Jul 11, 2008, at 9:17 AM, Matthew Moss wrote:

## Symbolify (#169)

#
   def symbolify i;def (s=i.to_s).delete _;'';end;s;end

# test
#
   nums = (0...1000).sort_by { rand }
   strs = nums.map { |n| symbolify(n) }

   strs.zip(nums).sort_by { rand }.each do |str, num|
     res = eval(str)
     raise "Not a string!" unless str.is_a? String
     raise "Invalid chars!" unless str.delete("?*()-").empty?
     raise "Decode failed!" unless res == num
   end

   puts "Passed!"

cfp:~/src/ruby > time ruby a.rb
Passed!

real 0m0.026s
user 0m0.021s
sys 0m0.005s

a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

## Symbolify (#169)

Fairly straightforward; handles negative and positive. I tried for the
shortest resulting encoding but fell a little short compared to some of
the other results . Thats likely due to my use of addition instead of
multiplication.

def symbolify(j)
    i = j.abs
    add = "--"
    unless $nums
        code = {?? => "??", ?- => "?-", ?) => "?)", ?( => "?(", ?* =>
"?*"}
        $nums = {}
        code.keys.each do |x|
            code.keys.each do |y|
                $nums[x-y] = "%s-%s" % [code, code[y]]
                $nums[x*y] = "%s*%s" % [code, code[y]]
                $nums[x**y] = "%s**%s" % [code, code[y]]
            end
        end
    end

    if $nums[i]
        eq = "%s%s%s" % [j < 0 ? "-(" : "", $nums[i], j < 0 ? ")" : ""]
        return eq
    end

    values = {}
    remove = 0
    $nums.keys.sort.reverse.each do |num|
        next unless num > 1
        if num < i and i % num == 0
            return "%s(%s)*(%s)" % [j<0 ? "-" :
"",$nums[num],symbolify(i/num)]
        end
        pow = 0
        pow += 1 while num**pow <= i
        values[num**(pow-1)] = [num,pow-1]
        remove = num**(pow-1) if remove < num**(pow-1)
    end

    base,pow = values[remove]
    equation = "(%s)**(%s)" % [$nums[base], symbolify(pow)]
    equation << add + symbolify(i-remove) if i - remove > 0
    j < 0 ? "-(" + equation + ")" : equation

end

···

--
Posted via http://www.ruby-forum.com/\.

Me too. My method body is one line, 25 characters. Did you manage to
stretch yours to 42? :slight_smile:

-A

···

On Fri, Jul 11, 2008 at 12:12 PM, ara.t.howard <ara.t.howard@gmail.com> wrote:

On Jul 11, 2008, at 9:17 AM, Matthew Moss wrote:

That is, the following test code should raise no exceptions:

  1000.times do |i|
    s = symbolify(i)
    raise "Not a string!" unless s.is_a? String
    raise "Invalid chars!" unless s.delete("?*()-").empty?

    x = eval(s)
    raise "Decode failed!" unless i == x
  end

i've got one line - tests pass.

> ## Symbolify (#169)

> Your task this week is to count. Yup, that's it.

> Oh, by the way... You may only use the characters `?`, `*`, `(`, `)`
> and `-`.

Excellent quiz. Full marks Matthew. :wink:

Somehow, with all the comparisons of how small solutions are, methinks
there are other techniques that I hadn't considered. Mine own solution
is short, but not *that* short.

Though I haven't attempted to shrink it yet, so maybe I can make it
smaller... Let's see...

Okay, I can see how this can be seriously abused. But I guess I asked
for it when I said "cheating encouraged!" No worries; it will be
interesting to see how people do. I just wonder if anyone will write a
solution that doesn't cheat. Or perhaps I'm still underestimating your
solutions? Guess I'll see on Monday.

Shortly after posting this, I realize that there could easily be ugly
cheats that pass this test as well...

You'd think I know Ruby a little better at this point in time... =)

I've done some translating of these tests into test/unit (and some
meta-programming to help me test multiple solutions easily. Here's
what the end of my solution file looks like:

···

On Jul 11, 12:57 pm, Matthew Moss <matthew.m...@gmail.com> wrote:

Anyway, here's another test:

nums = (0...1000).sort_by { rand }
strs = nums.map { |n| symbolify(n) }

strs.zip(nums).sort_by { rand }.each do |str, num|
res = eval(str)
raise "Not a string!" unless str.is_a? String
raise "Invalid chars!" unless str.delete("?*()-").empty?
raise "Decode failed!" unless res == num
end

puts "Passed!"

###
require 'test/unit'

class TestSymbolify < Test::Unit::TestCase
  @test_number = 1

  def self.solution_test(module_name, range, test=:delayed_eval_tests)
    class_eval <<-EOD
      def test_#{@test_number}_#{module_name.to_s.gsub('::','_')}
        Object.send(:include, #{module_name})
        #{test}(#{range})
      end
    EOD
    @test_number += 1
  end

  def symbolify_assertions_for(string, integer)
    assert_kind_of(String, string)
    assert_match(/\A[?*()-]*\Z/, string)
    assert_equal(integer, eval(string))
  end

  def cheat_friendly_tests(range)
    range.each do |i|
      s = symbolify(i)
      symbolify_assertions_for(s,i)
    end
  end

  def delayed_eval_tests(range)
    nums = range.sort_by { rand }
    strs = nums.map { |n| symbolify(n) }

    strs.zip(nums).sort_by { rand }.each do |s, i|
      symbolify_assertions_for(s,i)
    end
  end

  # The actual tests
  solution_test(Symbolify::Integers, -1000..1000)
  solution_test(Symbolify::NaturalNumbers, 0..1000)
  solution_test(Symbolify::Cheating,
-1000..1000, :cheat_friendly_tests)
end
###

It might be a little much... but I *was* having fun.

Chris