4

When programming ruby I always find myself doing this:

a = [a, b].min

This means compare a and b and store the smallest value in a. I don't like writing the code above as I have to write a twice.

I know that some non-standard dialects of C++ had an operator which did exactly this

a <?= b

Which I find very convenient. But I'm not really interested in the operator as much as I'm in the feature of avoiding repetition. I would also be happy if I could write

a.keep_max(b)

a can be a quite long variable, like my_array[indice1][indice2], and you don't want to write that twice.

I did alot of googling on this and found no result, hopefully this question will pop up and be useful for others aswell.

So, is there any non-repeitive way to express what I want in ruby?

Tarrasch
  • 10,199
  • 6
  • 41
  • 57
  • 1
    IMO re-binding values (without an excellent reason for doing it) is a bad choice, just create a new variable with a different name. Think about it: now `a` has value 2, now some magic, `a` has value 1... that kind of code is harder to understand and debug. But, of course, I am a functional bigot ;-) – tokland Feb 03 '12 at 13:36
  • @tokland, yea perhaps. But for typical dynamic programming code, say floyd-warshall, you often do exactly what I expressed. After all assignments can be the power of imperative languages. – Tarrasch Feb 03 '12 at 13:41
  • 1
    Fair enough, when you are inside an imperative loop you have no option but to reuse variable names (and I guess a functional implementation of that algorithm may be inefficent in Ruby). My note was just a prevention against lazy coding that reuses variable names just because. – tokland Feb 03 '12 at 13:48
  • 1
    @tokland, Yep. what you say is completely right. I'm a haskell fan, but I believe assignments are not always evil! Furthermore ruby isn't primarily designed for functional style (afaik). – Tarrasch Feb 03 '12 at 17:23

4 Answers4

2

I don't understand your question. You can always do something like this ...

module Comparable
  def keep_min(other)
     (self <=> other) <= 0 ? self : other
  end

  def keep_max(other)
     (self <=> other) >= 0 ? self : other
  end
end

1.keep_min(2)
=> 1

1.keep_max(2)
=> 2

Well, that won't work for all objects with <=> because not all of them are implementing Comparable, so you could monkey-patch Object.

Personally I prefer clarity and tend to avoid monkey-patching. Plus, this clearly is a binary predicate, just like "+", therefore method-chaining doesn't necessarily make sense so I prefer something like this to get rid of that array syntax:

def min(*args)
   args.min
end

def max(*args)
   args.max
end

min(1, 2)
=> 1

max(1, 2)
=> 2

But hey, I'm also a Python developer :-)

Alexandru Nedelcu
  • 8,061
  • 2
  • 34
  • 39
2

What you would like to do is in fact not possible in ruby (see this question). I think the best you can do is

def max(*args)
  args.max
end

a = max a, b
Community
  • 1
  • 1
Jakub Hampl
  • 39,863
  • 10
  • 77
  • 106
  • The correct answer seems indeed to be that it simply isn't possible without `eval` tricks (which another question mentioned, thanks @kentor). – Tarrasch Feb 04 '12 at 18:12
1

You can define your own method for it:

class Object
  def keep_max(other)
    [self, other].max
  end
end

a = 3
b = 7
puts a.keep_max(b)

But you should be careful defining methods on Object as it can have unpredictable behaviour (for example, if objects cannot be compared).

Ineu
  • 1,363
  • 9
  • 15
0
def keep_max(var, other, binding)
  eval "#{var} = [#{var}, #{other}].max", binding
end

a = 5 
b = 78
keep_max(:a, :b, binding)
puts a
#=> 78

This basically does what you want. Take a look at Change variable passed in a method

Community
  • 1
  • 1
kentor
  • 1,104
  • 2
  • 9
  • 19