Odd functional programming question

Ok this is probably not really functional programming but I was just
noodling about with Ruby when I thought "it would be quite nice if I
could do X". In this case X is as follows:

def X(text, action)
  x.action
end

X("abcedf", size)

=> 6

X("abcdef", upcase)

=> "ABCDEF"

X("abcdef", gsub(/[aeiouy]/, '_'))

=> "_bcd_f"

Quite clearly this does not actually work, otherwise I wouldn't be
asking. But is there some way in Ruby to do something like this?

Typical I solved inspiration struck after I hit send:

def X(text, todo, *args)
  text.send(todo, *args)
end

puts X("abcdefgh", 'downcase')
puts X("abcdefgh", 'gsub', /[aeiouy]/, "_")

If only I had waited a few moments more :frowning:

Any other ideas gratefully received.

Looks like a case for #instance_exec.

def foo(x, &action)
   x.instance_exec(&action)
end

p foo("abcedf") {size}
p foo("abcdef") {upcase}
p foo("abcdef") {gsub(/[aeiouy]/, '_')}

# unlike #send, you can have more complex code than one single
# method call
p foo("abcdef") {self+reverse}

# also #instance_exec lets you pass thru args, which gives you
# some flexibility in how you factor actions
def bar(x, *args, &action)
   x.instance_exec(*args, &action)
end

p bar("abcdef", /[aeiouy]/, '_') {|pat, str| gsub(pat, str)}

···

On 09/06/2010 06:40 AM, Peter Hickman wrote:

Ok this is probably not really functional programming but I was just
noodling about with Ruby when I thought "it would be quite nice if I
could do X". In this case X is as follows:

def X(text, action)
   x.action
end

X("abcedf", size)

=> 6

X("abcdef", upcase)

=> "ABCDEF"

X("abcdef", gsub(/[aeiouy]/, '_'))

=> "_bcd_f"

Quite clearly this does not actually work, otherwise I wouldn't be
asking. But is there some way in Ruby to do something like this?

That's The Right Way To Do It. For methods which take a block you'll need to
pass that through, and it's slightly more efficient to use symbols instead
of strings (if you pass a string to send, it will have to convert it to a
symbol for you).

def X(text, todo, *args, &blk)
  text.send(todo, *args, &blk)
end

puts X("abcdefgh", :downcase)
puts X("abcdefgh", :each_byte) { |i| puts i }

···

On Mon, Sep 06, 2010 at 10:50:35PM +0900, Peter Hickman wrote:

Any other ideas gratefully received.

Oh yes, that looks very interesting

Thanks a lot

···

On 6 September 2010 18:58, Joel VanderWerf <joelvanderwerf@gmail.com> wrote:

Looks like a case for #instance_exec.

def X(text, todo, *args, &blk)

vs

puts X("abcdefgh", :each_byte) { |i| puts i }

some weird edges of ruby :slight_smile:

···

On Mon, Sep 6, 2010 at 10:11 PM, Brian Candler <B.Candler@pobox.com> wrote:

Actually I'd say _this_ definition of X is completely superfluous
since it does not add anything to #send and so only helps obfuscate
code and slows down execution.

Kind regards

robert

···

On Mon, Sep 6, 2010 at 4:11 PM, Brian Candler <B.Candler@pobox.com> wrote:

On Mon, Sep 06, 2010 at 10:50:35PM +0900, Peter Hickman wrote:

Any other ideas gratefully received.

That's The Right Way To Do It. For methods which take a block you'll need to
pass that through, and it's slightly more efficient to use symbols instead
of strings (if you pass a string to send, it will have to convert it to a
symbol for you).

def X(text, todo, *args, &blk)
text.send(todo, *args, &blk)
end

puts X("abcdefgh", :downcase)
puts X("abcdefgh", :each_byte) { |i| puts i }

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

It was just a dummy method to get a feel how the code would be used.
What I am using it for is a method to compare two pieces of data with
a variety of optional preprocessing. This way I can put the
preprocessing (of which there could be several variants) in an array,
as [ :gsub, /[aeiouy/, '_' ] etc, and iterate over the data and the
preprocessors and get the fitness scores out of the X method.

I will mean that I have one X method rather than X1, X2 ... Xn which
are all just minor variations on X.

···

On 6 September 2010 15:52, Robert Klemme <shortcutter@googlemail.com> wrote:

Actually I'd say _this_ definition of X is completely superfluous
since it does not add anything to #send and so only helps obfuscate
code and slows down execution.

Where do you take the sequence of operations from? Maybe it's simpler
(but certainly more efficient) to just load a Ruby file and execute
the code fetched from there. Or use evil #eval to compile the code
for later execution. You could then store it in a lambda and execute
it from there.

Kind regards

robert

···

On Mon, Sep 6, 2010 at 5:07 PM, Peter Hickman <peterhickman386@googlemail.com> wrote:

On 6 September 2010 15:52, Robert Klemme <shortcutter@googlemail.com> wrote:

Actually I'd say _this_ definition of X is completely superfluous
since it does not add anything to #send and so only helps obfuscate
code and slows down execution.

It was just a dummy method to get a feel how the code would be used.
What I am using it for is a method to compare two pieces of data with
a variety of optional preprocessing. This way I can put the
preprocessing (of which there could be several variants) in an array,
as [ :gsub, /[aeiouy/, '_' ] etc, and iterate over the data and the
preprocessors and get the fitness scores out of the X method.

I will mean that I have one X method rather than X1, X2 ... Xn which
are all just minor variations on X.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

At the moment this is the exploration of an idea and I want to stop
the code becoming cluttered up with minor code variants. When I find
out what works then I will look into the performance of the code. But
at this point ease of exploration is the key.

Thanks for your help.

···

On 6 September 2010 16:22, Robert Klemme <shortcutter@googlemail.com> wrote:

Where do you take the sequence of operations from? Maybe it's simpler
(but certainly more efficient) to just load a Ruby file and execute
the code fetched from there. Or use evil #eval to compile the code
for later execution. You could then store it in a lambda and execute
it from there.