[SUMMARY] One-Liners Mashup (#177)

Thanks to everyone who joined in on the one-liners mashup. I think
part of the challenge to the mashup was coming up with new problems.
It seems that, unless you've already solved the problem, it takes a
bit of intuition to come up with a problem that isn't trivial, but
also that can be done within a typical line or two. All the problems
presented seemed to fit right in.

I'm not going to do a regular summary here: the discussion on Ruby
Talk pretty much _is_ the summary. I will run through all the problems
very quickly, just pulling out the solution that appealed to me the
most in each case.

The problem:

    Write a single line method for Array that does this:
      > [:one, "two", 4].repeat(3)
      => [:one, :one, :one, "two", "two", "two", 4, 4, 4]

"The Nice" solution (I agree):

    def repeat(n)
      inject([]) { |a,e| a.concat [e]*n }
    end

The problem:

    Print out a Sierpinski carpet.

The shorter (but still kinda long) solution:

    def carpet(n)
      n==0?"#\n":carpet(n-1).map{|l| ['\0\0\0','\0
\0','\0\0\0'].map{|c| l.gsub(/#| /,c)}}.join
    end

The problem:

    Given the class:

      class Data2D
        def initialize
          @data = [ ] # in row major form
        end

        def add_row(*row)
          @data << row
        end
      end

    And this setup for an object:

      data = Data2D.new
      data.add_row(1, 2, 3, 4)
      data.add_row(5, 6, 7, 8)

    define a [] method that makes this form of access possible:
      data[2][1] # => 7

The tricksy call-you-later solution:

    class Data2D
      def [](x) lambda { |y| @data[y][x] }
    end

The problem:

    Write an []= method to solve the following:
      obj = YourClass.new
      obj['answer'] = 42
      obj.answer # => 42

The what-the-heck-with-all-the-eval solution:

    class YourClass
      def []=(f, v)
        class << self; self end.instance_eval{ attr_accessor f };
instance_eval "@#{f}=v"
      end
    end

The problem:

    Given one or more input filenames on the command line, report the
number of unique IP addresses
    found in all the data.

The command-line solution:

    ruby -e 'p ARGF.read.scan(/\d{1,3}(\.\d{1,3}){3}/).uniq.size'

The problem:

    Here's something simple: define method roll(n, s) to roll a s-sided
    die n times and display a bar graph of the results. So roll(20, 4)
    might look like this:
     1|#####
     2|#####
     3|######
     4|####

A random solution:

    def roll(n,s)
      (1..n).map { |x| "#{x}|#{'#' * (1 + rand(s))}" } * "\n"
    end

The problem:

    Provide a one-liner that can wrap a body of text at a requested
maximum length.

The "Didn't we do this before?" solution:

    text.gsub(/(.{1,80})\s|(\w{80})/, "\\1\\2\n")

The problem:

    Sort an array of words by the words' values where a word's value is the
    sum of the values of its letters and a letter's value is its position in
    the alphabet. So the value of "Hello" is 8+5+12+12+15.

The "I mutated two solutions into one I like the bestest" solution:

    class Array
      def sortval()
        sort_by { |x| x.upcase.sum - x.length * ?@ }
      end
    end

The problem:

    Write a function per(n) which returns the periodicity of 1/n, i.e.
      per(3) => 1
      per(4) => 0
      per(7) => 6
      per(11) => 2

The "Ummm... yeah... how do these work?" solution:

    def per(n, b=10)
      i=1;x=b;h={};loop {x=x%n*b;break 0 if x==0;h[x]?(break
i-h[x]):h[x]=i; i+=1}
    end

The problem:

    Return the power set of an Array's elements.

The "Don't get up, I'll take care of it" solution:

    class Array
      def powerset
        inject([[]]) { |a,e| a + a.map { |b| b+[e] } }
      end
    end

The problem:

    Assuming you have an array of numeric data, write a method that
returns an array of progressive
    sums. That is:
      prog_sum( [1, 5, 13, -6, 20] ) => [1, 6, 19, 13, 33]

The obligatory inject solution:

    def prog_sum(ary)
      ary.inject([0, []]) {|(s, a), i| [s+i, a<<(s+i)]}.last
    end

The problem:

    Given an s-expression, print it out as a tree, where [:a,
    :b, :c, :d] is the node with parent a and children b, c and d

    [:foo, [:bar, [:baz, :quux], :hello, :world], :done] #=>

    foo
    > -- bar
    > > -- baz
    > > > -- quux
    > > -- hello
    > > -- world
    > -- done

The last solution of the summary... solution:

    class Array
      def to_s
        collect{|x| x.to_s.gsub(/\n/,"\n| ")}.join("\n|-- ")
      end
    end

Any unsolved problems? You bet! Here are the problems that didn't get
answered, in case you've got an itch for more.

    1. Given a text from STDIN and a regexp, print each line which (in part)
    matches regexp with two preceding and two successional lines. Do not output
    a line more than once.

    2. Starting with an array, find the first permutation of the elements of
    that array that is lexicographically greater than (i.e. sorts after)
    the given array.

    3. Write a oneliner next_fib(n) which gives the smallest Fibonacci number
    greater than n.

    4. Given an epsilon, compute PI to that precision.

···

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

def carpet\(n\)
  n==0?&quot;\#\\n&quot;:carpet\(n\-1\)\.map\{|l| \[&#39;\\0\\0\\0&#39;,&#39;\\0

\0','\0\0\0'].map{|c| l.gsub(/#| /,c)}}.join
end

If you saw this the way I saw it here, this is an unfortunate line-
wrap. That array in the first map call should be:

    ['\0\0\0','\0 \0','\0\0\0']

Matthew Moss wrote:

The problem:

    Write a function per(n) which returns the periodicity of 1/n, i.e.
      per(3) => 1
      per(4) => 0
      per(7) => 6
      per(11) => 2

The "Ummm... yeah... how do these work?" solution:

    def per(n, b=10)
      i=1;x=b;h={};loop {x=x%n*b;break 0 if x==0;h?(break
i-h):h=i; i+=1}
    end

It works like this:
http://img243.imageshack.us/img243/9296/perwt4.png
It simply divides until the remainder repeats itself (storing the position
where each remainder occured in a hash) and then returns the difference
between the first position where the remainder occured (i.e. the start of the
period) and the current position.

···

--
Jabber: sepp2k@jabber.org
ICQ: 205544826

#...
# def prog_sum(ary)
# ary.inject([0, []]) {|(s, a), i| [s+i, a<<(s+i)]}.last
# end

non-inject:

  prev=0; ary.map{|x| prev+=x}

···

From: Matthew Moss [mailto:matthew.moss@gmail.com]

Neat :slight_smile:

martin

···

On Fri, Sep 26, 2008 at 1:48 AM, Sebastian Hungerecker <sepp2k@googlemail.com> wrote:

It works like this:
ImageShack - Best place for all of your image hosting and image sharing needs
It simply divides until the remainder repeats itself (storing the position
where each remainder occured in a hash) and then returns the difference
between the first position where the remainder occured (i.e. the start of the
period) and the current position.

Sebastian Hungerecker wrote:

It works like this:
ImageShack - Best place for all of your image hosting and image sharing needs

Well, almost. "h[30] - i = 6" should of course be "i - h[30] = 6".

···

--
Jabber: sepp2k@jabber.org
ICQ: 205544826