chaining comparisons

J

Jason Creighton

As you say, there is only one true; but you still can save state in it:

irb(main):001:0> true.instance_eval {@x = 1}
1
irb(main):002:0> true.instance_eval {@x}
1

Not that it's relevant to the 1 < 2 < 3 discussion, but it's kinda cute...

Yeah, it is kinda cute. How does that work? It works for Fixnum objects,
too.....
=> 25

How can that work when Fixnum is stored in VALUE? And that's another thing:
VALUE is either an immediate value (Fixnum/true/false/nil) or a pointer to an
object. Doesn't that mean there's only 31 bits (on 32 bit machins) available
for the pionter? How can that work? I always thought that pointers would have
to be able to cover the whole address space....

Jason Creighton
 
T

ts

J> How can that work when Fixnum is stored in VALUE? And that's another thing:
J> VALUE is either an immediate value (Fixnum/true/false/nil) or a pointer to an
J> object. Doesn't that mean there's only 31 bits (on 32 bit machins) available
J> for the pionter?

You have effectively only 31 bits available for an ID, but not for the
reason that you think. This will give you the maximum number of ID that
you can have in a script.

On a 32 bits machins, you can have only (2**20 - 1) ID for a script, if
I'm right


Guy Decoux
 
R

Robert Klemme

An alternative

def less(*args); compare_chain(proc{|a,b|a<b}, *args); end
def greater(*args); compare_chain(proc{|a,b|a>b}, *args); end

def compare_chain(comp, *args)
last = nil

args.each do |i|
return false if last and not comp.call(last,i)
last = i
end

true
end

less 1,10,22
less 2,1,14
less -2,1,14
greater 10,3,-100
greater 2,1,14

robert


Simon Strandgaard said:
You can do this:

if [1, 2, 3].ordered?
puts "ok"
end


It will require that you extend the Array class yourself, like this:

expand -t4 b.rb
class Array
def ordered?
self == sort
end
end

(I'm a lazy typist :)

Work smarter, Not harder.
I know sort, But this solution didn't come to mind.

Both versions generate a temporary copy of the array, e.g. self[1..-1] does
that too. But you can avoid it:

Yes. Avoiding temporary copy were my goal when I started writing it.
As you can see I apparently forgot it in the hurry :)


[snip enum#ordered?]
You can then check whether all the lines in a file are ordered, for example,
without reading it into memory. (I am a big fan of Enumerable :)

Enum is Nice.

Some more thoughts on #ordered?

Supplying an operator, could be useful?

[3, 2, 1].ordered? :> #=> true
[2, 2, 2].ordered? :> #=> false
[2, 2, 2].ordered? :>= #=> true


Supplying an block could also be useful?

[2, 2, 2].ordered? { |a, b| (((a-b) ^ (b-a)) % 3) == 0 }
#=> true

[1, 2, 3].ordered? { |a, b| (((a-b) ^ (b-a)) % 3) == 0 }
#=> false
 
S

Simon Strandgaard

An alternative

def less(*args); compare_chain(proc{|a,b|a<b}, *args); end
def greater(*args); compare_chain(proc{|a,b|a>b}, *args); end

def compare_chain(comp, *args)
last = nil

args.each do |i|
return false if last and not comp.call(last,i)
last = i
end

true
end

less 1,10,22
less 2,1,14
less -2,1,14
greater 10,3,-100
greater 2,1,14

OK.. this is the nicest solution so far :)

I didn't thought of using proc's for binary compares.
 
R

Robert Klemme

OK.. this is the nicest solution so far :)

I've found an even nicer solution that allows user defined comparisons
nicely:

def less(*args); check_chain(*args){|a,b|a<b}; end
def greater(*args); check_chain(*args){|a,b|a>b}; end

def check_chain(*args, &comp)
raise "Block needed" unless comp

last = nil

args.each do |i|
return false if last and not comp.call(last,i)
last = i
end

true
end

less 1,10,22
less 2,1,14
less -2,1,14
greater 10,3,-100
greater 2,1,14

check_chain( 2, 4, 16 ){|a,b| a*a==b}
check_chain( 2, 4, 17 ){|a,b| a*a==b}

The thing that bugged my about the last one was the first parameter to
compare_chain() which prevented nice user defined checks.
I didn't thought of using proc's for binary compares.

The proposed implementation of ordering? inspired my. Though while
writing this I see a more general pattern here that reminds a bit of
Enumerable#inject (Ruby 1.8):

module Enumerable
def connect(coll)
last = nil
first = true

each do |i|
if first
first = false
else
coll = yield(coll,last,i)
end

last = i
end

coll
end
end

def less(*args); args.connect( true ){|c,a,b|c && a<b}; end
def greater(*args); args.connect( true ){|c,a,b|c && a>b}; end

[1, 2, 3].connect( true ){|coll,a,b|coll && a<b}
[2, 4, 16].connect( true ){|coll,a,b|coll && a*a==b}

These versions are less efficient for sequences that fail the test but one
might consider to change the implementation to stop if coll is false or
nil.

Method Enumerable#connect might even be considert a library addenum, IMHO.
Maybe inject might be changed to yield one more parameter the way it is
shown here. What does Matz think? What do others think?

Regards

robert
 
R

Robert Klemme

(Now I start already at 36 talking to myself...)
Method Enumerable#connect might even be considert a library addenum, IMHO.
Maybe inject might be changed to yield one more parameter the way it is
shown here. What does Matz think? What do others think?

I'd like to propose a slightly different version of Enumerable#inject which
uses a sliding window on the enum and is compatible with the 1.8 version:

module Enumerable
def inject(val=nil, &bl)
raise "Block missing" unless bl

size = bl.arity
raise "Need block with at least 1 argument" if size == 0

size = size < 0 ? 1+size : size-1
args=[]

each do |e|
args.push e

if args.length == size
val = yield val, *args
args.shift
end
end

val
end
end


Now, apart from the usual inject things like sum, product and max
calculations you can do a lot of nice things like

irb(main):374:0* a=[1, 20, 30, 40, 41, 42, 88, 123]
=> [1, 20, 30, 40, 41, 42, 88, 123]
irb(main):375:0> b=[]
=> []
irb(main):376:0> # SMOOTH
irb(main):377:0* sm=[]
=> []
irb(main):378:0> a.inject(0){|idx, x, y| sm[idx] = (x+y).to_f / 2; idx+1}
=> 7
irb(main):379:0> sm
=> [10.5, 25.0, 35.0, 40.5, 41.5, 65.0, 105.5]
irb(main):380:0> sm=[]
=> []
irb(main):381:0> a.inject(0){|idx, x, y, z| sm[idx] = (x+y+z).to_f / 3;
idx+1}
=> 6
irb(main):382:0> sm
=> [17.0, 30.0, 37.0, 41.0, 57.0, 84.33333333]
irb(main):383:0> # WEIGHTED SMOOTHING
irb(main):384:0* sm=[]
=> []
irb(main):385:0> a.inject(0){|idx, x, y, z| sm[idx] = (x+y+y+z).to_f / 4;
idx+1}
=> 6
irb(main):386:0> sm
=> [17.75, 30.0, 37.75, 41.0, 53.25, 85.25]
irb(main):387:0> # TEST SORTED
irb(main):388:0* a.inject(true) {|cond, x, y| cond && x<y}
=> true
irb(main):389:0> b.inject(true) {|cond, x, y| cond && x<y}
=> true
irb(main):390:0> def ordered?(*args); args.inject(true) {|cond, x, y| cond
&& x<
y}; end
=> nil
irb(main):391:0> ordered? *a
=> true
irb(main):392:0> ordered? 1,2,4
=> true
irb(main):393:0>


What do you think?

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top