[QUIZ] Getting to 100 (#119)

I'm just glad you broke that code down for us. Now maybe I can read it to look smart when I write up the summary. :wink:

James Edward Gray II

···

On Apr 9, 2007, at 4:35 AM, Robert Dober wrote:

BTW I want to have my ball back James, or adjust my handicap please ;).

Robert Dober wrote:

···

On 4/9/07, Raj Sahae <rajsahae@gmail.com> wrote:

I'm sorry to be a noob on this, but can someone please explain to me
what this is doing. If it works, it must be genius, and I can't figure
it out.

Raj Sahae

Actually I have no idea whatsoever, this is normally a good starting point :wink:
irb is our friend of course, so let us hack away:

Thanks for the explanation Robert. After reading over your email, I also went into irb and stepped through the process even more thoroughly. I'm amazed at how changing the base and then using string translation makes the problem so simple.

I didn't claim that it saves memory by allocating less. It saves memory
by retaining less references, so it can GC earlier.

--Ken

···

On Mon, 09 Apr 2007 21:47:43 +0900, Christian Neukirchen wrote:

Ken Bloom <kbloom@gmail.com> writes:

On Mon, 09 Apr 2007 01:39:19 +0900, Christian Neukirchen wrote:

=begin

The quiz, then, is to solve this problem without thinking, instead
letting the computer think for you.

I did not intent to seriously submit this solution, but it can be
helpful as an example of how to use Ruby to solve a problem quickly
without thinking too much or locating Knuth on the bookshelve. Writing
this code took maybe ten minutes and happened step-by-step with
checking the immediate results.

Since there are only 168 solutions (254 if you allow a sign before the
first digit), brute-forcing is the simplest thing one can do.
Additional operations can be added by changing the base and mapping
the digits onto further operations. (Different digit order is not
that easy to implement and maybe be futile to do with brute-forcing.)
=end

(00000000..'22222222'.to_i(3)).map { |x| x.to_s(3).rjust(8, "0").
                                                   tr('012', '-+ ') }.
  find_all { |x| x.count("-") == 2 and x.count("+") == 1 }.

A less ram-intensive version of this would swap the map and find_all
calls as follows:

(00000000..'22222222'.to_i(3)).
  find_all { |x| x.to_s(3).count("0") == 2 and
     x.to_s(3).count("1") == 1 }.
  map { |x| x.to_s(3).tr('012', '-+ ').rjust(8," ")}.

(It doesn't keep maintain references to the bad combinations of
operators, allowing the GC to reclaim them earlier.)

Yes, but that confuses the application logic. I don't think it will
save memory compared to yours, you are #to_s'ing a lot more than me!

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Everything but the arbitrary ordering and number of digits.

And it can run in O(n) time
:wink:

It's using an algorithm based on a bogosort to generate the equation :slight_smile:
the hash based implementations run reasonably quick. The array based
one has to check for uniqueness each time, so it's rather poor
performance wise.

Basically
123456789 is interspersed with " " + and - randomly, inserted into the
hash and repeated until the size of the hash is 8^3 (which is the
number of unique formulas)

It's not written for speed, it's written for the computer to do all
the work, and the programmer to be well.. lazy!

--Kyle

Indeed you may -- apologies, I thought I had been back through all the
posts but didn't go back as far as yours.

So I think you should also be feeling very happy :slight_smile:

I do have one small piece of constructive criticism (if I may) since
you brought attention to your code:

  found = val == goal
  puts "*****************" if found_in_a_row == 0 && found
  puts "*****************" unless found_in_a_row == 0 || found
  puts "#{eqn} = #{val}" if verbose || goal == val
  found_in_a_row = found ? found_in_a_row + 1 : 0

Could also have been written:

  found = val == goal
  puts "*****************" if found
  puts "#{eqn} = #{val}" if verbose || goal == val
  puts "*****************" if found
  found_in_a_row = found ? found_in_a_row + 1 : 0

For the same number of lines, I find the second much easier to follow
without having to remember any state for the second time around.
There does not seem to be any advantage in placing the asterisk lines
together. (?)

As another aside, I'm in two minds as to whether it's necessary to
provide such a complete set of parameters (and comments) for these
solutions. On the one hand it sets a good example to newcomers (and
it satisfies our quest for perfection) but on the other it does tend
to obscure some of the more interesting details. It's like you're
damned if you do and damned if you don't - the difficulty is finding
the right balance.

I think there's room for both kinds of posts but certainly the shorter
ones seem more appealing even if longer ones do use the same
underlying methods. I also like to work at making a solution more
generic for future purposes but I've come to the conclusion that
(unless the extra credits mention it) there's no point because I'm
only going to prejudice my solution.

In the real world I would go for the more generic code and proper
comments any day but for the purposes of the quiz I like to see
solutions that do just as much as is asked of them and ideally fit on
one page of my screen.

···

On 09/04/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

On 4/8/07, Marcel Ward <wardies@gmail.com> wrote:
> On 08/04/07, Carl Porth <badcarl@gmail.com> wrote:
> > After going back and reading the current solutions, I like Ken Bloom's
> > each_partition method. It's much cleaner than my combinations method.
>
> I agree, very clean and efficient. The three lines that put it all
> together using #zip are what makes this work for me:
>
> Digits.each_partition(Operators.length+1) do |digitspart|
> OperatorPerms.each do |operatorsperm|
> expression=digitspart.zip(operatorsperm).flatten.join
> ...
>
> I think I would be feeling very happy if I'd submitted this solution :slight_smile:

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.

--
Marcel

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Indeed! Well, isn't too coincidental after all for me to have used
the same mechanism. Great minds must think alike, no?

Harrison Reiser

···

On Apr 9, 6:18 am, "Rick DeNatale" <rick.denat...@gmail.com> wrote:

On 4/8/07, Marcel Ward <ward...@gmail.com> wrote:

> On 08/04/07, Carl Porth <badc...@gmail.com> wrote:
> > After going back and reading the current solutions, I like Ken Bloom's
> > each_partition method. It's much cleaner than my combinations method.

> I agree, very clean and efficient. The three lines that put it all
> together using #zip are what makes this work for me:

> Digits.each_partition(Operators.length+1) do |digitspart|
> OperatorPerms.each do |operatorsperm|
> expression=digitspart.zip(operatorsperm).flatten.join
> ...

> I think I would be feeling very happy if I'd submitted this solution :slight_smile:

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.

:wink:

···

On 4/9/07, James Edward Gray II <james@grayproductions.net> wrote:

On Apr 9, 2007, at 4:35 AM, Robert Dober wrote:

> BTW I want to have my ball back James, or adjust my handicap
> please ;).

I'm just glad you broke that code down for us. Now maybe I can read
it to look smart when I write up the summary. :wink:

James Edward Gray II

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

I typically compose my solution on Friday afternoon, and sit on it for
two days before posting to ruby-talk. When I post on Sunday, I do so
before reading any other solutions out there. After posting, I browse the
solutions, marvel at the clever ones, and usually notice that a bunch of
people have come up with the same idiomatic solution as me, thereby
suggesting that:
1) those people are also interested in clear idiomatic code
2) I'm unlikely to be the one summarized.

--Ken

···

On Mon, 09 Apr 2007 21:18:16 +0900, Rick DeNatale wrote:

On 4/8/07, Marcel Ward <wardies@gmail.com> wrote:

On 08/04/07, Carl Porth <badcarl@gmail.com> wrote:
> After going back and reading the current solutions, I like Ken
> Bloom's each_partition method. It's much cleaner than my
> combinations method.

I agree, very clean and efficient. The three lines that put it all
together using #zip are what makes this work for me:

Digits.each_partition(Operators.length+1) do |digitspart|
  OperatorPerms.each do |operatorsperm|
     expression=digitspart.zip(operatorsperm).flatten.join
...

I think I would be feeling very happy if I'd submitted this solution :slight_smile:

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Thanks for another fun Ruby Quiz. Here is my take on it, I did not
see other solutions that used String formatting to generate the
expressions.

class String
  def unique_permutations
    # modified to get unique permutations
    # from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/139858
    # which says it was inspired by a RubyQuiz! :slight_smile:
    return [self] if self.length < 2
    perms = Hash.new

    0.upto(self.length - 1) do |n|
      #rest = self.split('')
      rest = self.split(//u) # for UTF-8 encoded strings
      picked = rest.delete_at(n)
      rest.join.unique_permutations.each { |x| perms[picked + x] =
nil }
    end

    perms.keys
  end
end

digits = ARGV[0]
ops = ARGV[1]
target = ARGV[2].to_i

# pad ops list with spaces to match the number of slots between the
digits
ops = ops + " " * (digits.size - ops.size - 1)

# build a format string with slots between the digits
digits = digits.split("").join("%s")

operator_perms = ops.unique_permutations
operator_perms.each do |p|
  # build expression by inserting the ops into the format string,
  # after converting spaces to empty strings
  exp = digits % p.split("").map{|x|x.chomp(" ")}
  val = eval(exp)
  puts "*******************" if val==target
  puts exp + " = " + val.to_s
  puts "*******************" if val==target
end
puts
puts "%d possible equations tested" % operator_perms.size

Regards,
Paul

Not quite the same I think. I wrote it the way I did to cover the
(admittedly probably rare case where two solutions occur one after
the other. That's why the variable found_in_a_row is there.

I wanted to see:

···

On 4/9/07, Marcel Ward <wardies@gmail.com> wrote:

On 09/04/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

I do have one small piece of constructive criticism (if I may) since
you brought attention to your code:

  found = val == goal
  puts "*****************" if found_in_a_row == 0 && found
  puts "*****************" unless found_in_a_row == 0 || found
  puts "#{eqn} = #{val}" if verbose || goal == val
  found_in_a_row = found ? found_in_a_row + 1 : 0

Could also have been written:

  found = val == goal
  puts "*****************" if found
  puts "#{eqn} = #{val}" if verbose || goal == val
  puts "*****************" if found
  found_in_a_row = found ? found_in_a_row + 1 : 0

For the same number of lines, I find the second much easier to follow
without having to remember any state for the second time around.
There does not seem to be any advantage in placing the asterisk lines
together. (?)

********
  xxxxxx = 100
  yyyyyy = 100
********

instead of

********
  xxxxxx = 100
********
  yyyyyy = 100
********

As another aside, I'm in two minds as to whether it's necessary to
provide such a complete set of parameters (and comments) for these
solutions. On the one hand it sets a good example to newcomers (and
it satisfies our quest for perfection) but on the other it does tend
to obscure some of the more interesting details. It's like you're
damned if you do and damned if you don't - the difficulty is finding
the right balance.

I think there's room for both kinds of posts but certainly the shorter
ones seem more appealing even if longer ones do use the same
underlying methods. I also like to work at making a solution more
generic for future purposes but I've come to the conclusion that
(unless the extra credits mention it) there's no point because I'm
only going to prejudice my solution.

In the real world I would go for the more generic code and proper
comments any day but for the purposes of the quiz I like to see
solutions that do just as much as is asked of them and ideally fit on
one page of my screen.

I see it as an opportunity to document my thoughts in solving the
quiz, and thereby teach myself and others. The best way to learn is
to teach.

And, Frankly, I'm kind of an anti-golf advocate, except when I have a
club in my hands instead of a keyboard at my finger-tips.

Finally I figure that it's JEG II's choice as to how to summarize the solutions.

Chacun a son goût!

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

>> > After going back and reading the current solutions, I like Ken
>> > Bloom's each_partition method. It's much cleaner than my
>> > combinations method.
>>
>> I agree, very clean and efficient. The three lines that put it all
>> together using #zip are what makes this work for me:
>>
>> Digits.each_partition(Operators.length+1) do |digitspart|
>> OperatorPerms.each do |operatorsperm|
>> expression=digitspart.zip(operatorsperm).flatten.join
>> ...
>>
>> I think I would be feeling very happy if I'd submitted this solution :slight_smile:
>
> May I have the temerity to point out that I posted basically the same
> solution, which I posted two hours before Ken's.

I typically compose my solution on Friday afternoon, and sit on it for
two days before posting to ruby-talk. When I post on Sunday, I do so
before reading any other solutions out there. After posting, I browse the
solutions, marvel at the clever ones, and usually notice that a bunch of
people have come up with the same idiomatic solution as me, thereby
suggesting that:
1) those people are also interested in clear idiomatic code
2) I'm unlikely to be the one summarized.

Cheer up Ken;) Ruby Quiz - Method Auto Completion (#110)

···

On 4/11/07, Ken Bloom <kbloom@gmail.com> wrote:

On Mon, 09 Apr 2007 21:18:16 +0900, Rick DeNatale wrote:
> On 4/8/07, Marcel Ward <wardies@gmail.com> wrote:
>> On 08/04/07, Carl Porth <badcarl@gmail.com> wrote:

--Ken

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Thanks for another fun Ruby Quiz. Here is my take on it, I did not
see other solutions that used String formatting to generate the
expressions.

class String
  def unique_permutations
    # modified to get unique permutations
    # fromhttp://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/139858
    # which says it was inspired by a RubyQuiz! :slight_smile:
    return [self] if self.length < 2
    perms = Hash.new

    0.upto(self.length - 1) do |n|
      #rest = self.split('')
      rest = self.split(//u) # for UTF-8 encoded strings
      picked = rest.delete_at(n)
      rest.join.unique_permutations.each { |x| perms[picked + x] =
nil }
    end

    perms.keys
  end
end

Clever use of hashes. I wrote a String#each_unique_permutation, but
didn't think that it could be used like this.

# pad ops list with spaces to match the number of slots between the
digits
ops = ops + " " * (digits.size - ops.size - 1)

# build a format string with slots between the digits
digits = digits.split("").join("%s")

operator_perms = ops.unique_permutations
operator_perms.each do |p|
  # build expression by inserting the ops into the format string,
  # after converting spaces to empty strings
  exp = digits % p.split("").map{|x|x.chomp(" ")}

I like that you permute the ops string with extra spaces. Much
simpler than something like digits.each_ordered_partition.

Harrison Reiser

···

On Apr 9, 8:25 am, "paul" <nova...@gmail.com> wrote:

> For the same number of lines, I find the second much easier to follow
> without having to remember any state for the second time around.
> There does not seem to be any advantage in placing the asterisk lines
> together. (?)

Not quite the same I think. I wrote it the way I did to cover the
(admittedly probably rare case where two solutions occur one after
the other. That's why the variable found_in_a_row is there.

I wanted to see:

********
  xxxxxx = 100
  yyyyyy = 100
********

instead of

********
  xxxxxx = 100
********
  yyyyyy = 100
********

Aha, thanks for clarifying. I think this would be a prime candidate
for a comment. Before someone else gets their mits on the code and
starts refactoring inappropriately (like me).

> As another aside, I'm in two minds as to whether it's necessary to
> provide such a complete set of parameters (and comments) for these
> solutions. On the one hand it sets a good example to newcomers (and
> it satisfies our quest for perfection) but on the other it does tend
> to obscure some of the more interesting details. It's like you're
> damned if you do and damned if you don't - the difficulty is finding
> the right balance.
>
> I think there's room for both kinds of posts but certainly the shorter
> ones seem more appealing even if longer ones do use the same
> underlying methods. I also like to work at making a solution more
> generic for future purposes but I've come to the conclusion that
> (unless the extra credits mention it) there's no point because I'm
> only going to prejudice my solution.
>
> In the real world I would go for the more generic code and proper
> comments any day but for the purposes of the quiz I like to see
> solutions that do just as much as is asked of them and ideally fit on
> one page of my screen.

I see it as an opportunity to document my thoughts in solving the
quiz, and thereby teach myself and others. The best way to learn is
to teach.

I can't disagree with that - it's been my theory up until now but I
have no idea how useful it's been to others. If I'm just turning
people away with long posts then I'd prefer to shorten them down to
meet the audience's expectations better.

And, Frankly, I'm kind of an anti-golf advocate, except when I have a
club in my hands instead of a keyboard at my finger-tips.

Finally I figure that it's JEG II's choice as to how to summarize the solutions.

Chacun a son goût!

I'm definitely in favour of comments and readability. I guess I was
just verbalising some inner thoughts and wondering how others try to
find the right balance.

···

On 09/04/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

On 4/9/07, Marcel Ward <wardies@gmail.com> wrote:

--
Marcel

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

It can be tricky to know when to use them. A good comment can ease you right into some code. A bad comment can do the opposite and I even find that a good comment sometimes hurts completely obvious code.

In general though, I like seeing them in Ruby Quiz solutions for two reasons:

* It helps me understand the code. That's how I know if I want to write about it and it helps me know what to say if I do.

* It helps others understand all the solutions I don't cover. Always a big win, I think.

There's my two cents worth.

James Edward Gray II

···

On Apr 9, 2007, at 12:00 PM, Marcel Ward wrote:

On 09/04/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

Finally I figure that it's JEG II's choice as to how to summarize the solutions.

Chacun a son goût!

I'm definitely in favour of comments and readability. I guess I was
just verbalising some inner thoughts and wondering how others try to
find the right balance.

Marcel Ward wrote:

I see it as an opportunity to document my thoughts in solving the
quiz, and thereby teach myself and others. The best way to learn is
to teach.

I can't disagree with that - it's been my theory up until now but I
have no idea how useful it's been to others. If I'm just turning
people away with long posts then I'd prefer to shorten them down to
meet the audience's expectations better.

I say: go for long emails if you have to, but try to be as precise as you can. This has the "learning through teaching" effect, and helps to keep your emails at a manageable length.

It is my opinion, that those, who can't be bothered to read a long explanation, won't read the documentation either, which results in somebody who can't be educated in the first place.

Rest assured, if the topic interests me, I'll read a long email, too.

I'm definitely in favour of comments and readability. I guess I was
just verbalising some inner thoughts and wondering how others try to
find the right balance.

Well, if the code is readable and speaks for itself, comments can be omitted. Otherwise, the comments should explain what the code does, not what it should do.

I.e.:
#Opening a file
f = File.open("foo")

^

is superfluous.

#This does stuff to the passed argument bar
def do_stuff(bar)
   #doing stuff to bar
end
^

Isn't superfluous, and aids debugging if the thing doesn't do what it should. And you know that it doesn't do what it is supposed to do, because your tests tell you that it doesn't.

But such a thing merits it's own thread, I'd say.

···

--
Phillip "CynicalRyan" Gawlowski
http://cynicalryan.110mb.com/

Rule of Open-Source Programming #8:

Open-Source is not a panacea.

Weighing in about the comments and explanations of the quiz answers:
the comments are good, and important especially when using tricks.
Especially considering that I'm not alone in suggesting that
ruby-newbies I meet read old quizzes and the solutions that were
posted to learn more about Ruby. The thing is, simple, or not so
simple, code is sometimes best left uncommented so that the reader is
forced to parse it and figure out exactly what's going on. It aides
in their understanding.

And yes I know I'm horribly guilty of not entering enough comments in
myself, especially in some of the ugly code. :slight_smile:

--Kyle