Suggestion of Array#=== which improves case/when behaviour

D

Dmitry Vazhov

Hello,

We already have :=== operator defined in Module, Range, Regexp and Proc
which is great for case/when statement. For all other objects :=== mean
:==.

My suggestion is to extend Array with === method:

Original behaviour:
[1,2,3] === [1,2,3] #=> true
[1,2,3] === [1,2,4] #=> false
[1,2,3] === 2 #=> false

Adding code: -- this code is similar to Array#== , but uses :=== for
comparing each element
class Array
def ===( arg )
arg.is_a?(Array) && self.size == arg.size && (0...size).all?{|i|
self === arg }
end
end

After adding code:
[1,2,3] === [1,2,3] #=> true
[1,2,3] === 2 #=> false

[1,2,Object] === [1,2,3] #=> true
[1,2,/^some/] === [1,2,"some string"] #=> true
[1,2,[Symbol, Array]] === [1,2,[:key, []]] #=> true <---- this
is an example of deep structure matching

You can see other examples at
http://github.com/dmitryelastic/dumb-patterns-matching/blob/master/patterns_matching.rb
and http://github.com/dmitryelastic/dumb-patterns-matching
 
D

Dmitry Vazhov

"Set" class has meaning close to "Range" class. If we will define
Set#=== as

class Set
def ===( arg )
self.any?{|template| template === arg }
end
end

we will be able to write case/when code in haskell-like pattern-matching
style. here is an example of simple s-expression evaluator from my page:

Sexp = Set[Array, Numeric] # means - array or number
Boolean = Set[true, false]
Sexpbool = Set[Array] | Boolean # means - array or true or false


def evale(e) # function which will evaluate S-Expression
case e
when Numeric, Boolean then e
when [:-, Sexp] then - evale(e[1])
when [:-, Sexp, Sexp] then evale(e[1]) - evale(e[2])
when [:+, Sexp, Sexp] then evale(e[1]) + evale(e[2])
when [:*, Sexp, Sexp] then evale(e[1]) * evale(e[2])
when [:**, Sexp, Sexp] then evale(e[1]) ** evale(e[2])
when [:>, Sexp, Sexp] then evale(e[1]) > evale(e[2])
when [:if, Sexpbool, Sexp, Sexp] then evale(e[1]) ?evale(e[2]) :
evale(e[3])
when Object then fail("something went wrong")
end
end

def test_exp_eval
exp = [:*, [:-, 9, 2], [:+, 8, [:-, 2]]] # -> (9 - 2) * (2 + 4) = 42
assert_equal 42, evale(exp)
exp2 = [:if, true, 10, 20]
assert_equal 10, evale(exp2)
exp3 = [:if, [:>, [:**, 5, 5], 4000], 1, 2] # -> 2 , because 4000 >
5**5
assert_equal 2, evale(exp3)
end





Dmitry said:
Hello,

We already have :=== operator defined in Module, Range, Regexp and Proc
which is great for case/when statement. For all other objects :=== mean
:==.

My suggestion is to extend Array with === method:

Original behaviour:
[1,2,3] === [1,2,3] #=> true
[1,2,3] === [1,2,4] #=> false
[1,2,3] === 2 #=> false

Adding code: -- this code is similar to Array#== , but uses :=== for
comparing each element
class Array
def ===( arg )
arg.is_a?(Array) && self.size == arg.size && (0...size).all?{|i|
self === arg }
end
end

After adding code:
[1,2,3] === [1,2,3] #=> true
[1,2,3] === 2 #=> false

[1,2,Object] === [1,2,3] #=> true
[1,2,/^some/] === [1,2,"some string"] #=> true
[1,2,[Symbol, Array]] === [1,2,[:key, []]] #=> true <---- this
is an example of deep structure matching

You can see other examples at
http://github.com/dmitryelastic/dumb-patterns-matching/blob/master/patterns_matching.rb
and http://github.com/dmitryelastic/dumb-patterns-matching
 
B

Benoit Daloze

[Note: parts of this message were removed to make it a legal post.]

Hi,

It doesn't look too usefull to me like that. (I mean using classes to
compare).

For this purpose I wrote something like:

# Analyse arguments given to a method(*args)
#
# Exemple:
# def m(*args)
# case args
# when ARGS[Complex]
# m(Complex.new(1,1))
# when ARGS[Integer, Boolean]
# m(2, true)
# when ARGS[[Numeric, Float], String]
# m([1, 3.14], "Hello World!")
# when ARGS[[[Integer, Integer],[Float, Rational ]]]
# m( [[1 , 2 ],[3.0 , Rational(1,2)]])
# end
# end

module Boolean; end
class FalseClass; include Boolean; end
class TrueClass; include Boolean; end

class ARGS
def initialize(*constraints)
@constraints = constraints
end

def ARGS.[](*constraints)
ARGS.new(*constraints)
end

def match?(args)
return false unless @constraints.length == args.length
@constraints.each_with_index { |constraint, i|
case constraint
when Module
unless args.is_a?(constraint)
return false
end
when Array
unless args.is_a?(Array) &&
ARGS[*constraint].match?(args)
return false
end
end
}
true
end

def ===(args)
match?(args)
end
end

-------

Some mails ago, I also thought Array#=== implemented a OR-related test like
in:
case 2
when -1,1,2
...
end

But that seems to be more a feature of the "block" case.

So, I think this trick can be useful in special "cases", but I don't see
enough interest to do that for the Ruby core.

Any exemples more attractive ?
 
C

Caleb Clausen

I long ago did something similar, but much more extensive; I created a
large pattern-matching language/library for ruby data structures
called Reg. Instead of changing the existing Array#===, I created a
new class, Reg::Array, which has the functionality you want. A
Reg::Array can be constructed by enclosing the patterns of interest
between +[ and ].

You should have a look at Reg; you can install the gem:
gem install reg
or take a look at the github project, which contains newer (and buggier) code:
http://github.com/coatl/reg


"Set" class has meaning close to "Range" class. If we will define
Set#=== as

class Set
def ===( arg )
self.any?{|template| template === arg }
end
end

Personally, I would rather see Set#=== be an alias for include?. The
alternation semantics that you want here are provided in Reg by
Reg::Or, which is usually created by gluing together individual
matchers that you want with the | operator. So it'd look like:
Sexp = Array|Numeric
instead of:
Sexp = Set[Array, Numeric]
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

I would absolutely love if Array recursively performed #=== on its
arguments. AFAICT Array#=== is practically identical to Array#==

I long ago did something similar, but much more extensive; I created a
large pattern-matching language/library for ruby data structures
called Reg. Instead of changing the existing Array#===, I created a
new class, Reg::Array, which has the functionality you want. A
Reg::Array can be constructed by enclosing the patterns of interest
between +[ and ].

You should have a look at Reg; you can install the gem:
gem install reg
or take a look at the github project, which contains newer (and buggier)
code:
http://github.com/coatl/reg


"Set" class has meaning close to "Range" class. If we will define
Set#=== as

class Set
def ===( arg )
self.any?{|template| template === arg }
end
end

Personally, I would rather see Set#=== be an alias for include?. The
alternation semantics that you want here are provided in Reg by
Reg::Or, which is usually created by gluing together individual
matchers that you want with the | operator. So it'd look like:
Sexp = Array|Numeric
instead of:
Sexp = Set[Array, Numeric]
 
D

David A. Black

Hi --

I would absolutely love if Array recursively performed #=== on its
arguments. AFAICT Array#=== is practically identical to Array#==

It's an interesting idea but I wonder how useful it would be, compared
to having case equality be essentially == for Arrays. I just can't
imagine that many cases of, say:

case array
when [/abc/, Fixnum, "hi"]

etc. I guess it might occasionally be used for checking regex matches
on a bunch of strings at the same time:

case user_data
when [first_name_re, last_name_re, age_re, email_re]

or something -- but even in such a case, you'd probably want to do it
in such a way that you could isolate which one went wrong.

On the other hand, something like:

case user_data
when other_user_data

seems like a more likely comparison scenario.


David

--
David A. Black
Senior Developer, Cyrus Innovation Inc.
THE COMPLEAT RUBYIST, Ruby training with Black/Brown/McAnally!
January 22-23, Tampa, Florida
Info and registration at http://www.thecompleatrubyist.com
 
R

Rick DeNatale

Hi --

I would absolutely love if Array recursively performed #=3D=3D=3D on its
arguments. =A0AFAICT Array#=3D=3D=3D is practically identical to Array#=
=3D=3D

It's an interesting idea but I wonder how useful it would be, compared
to having case equality be essentially =3D=3D for Arrays. I just can't
imagine that many cases of, say:

=A0case array
=A0when [/abc/, Fixnum, "hi"]

My imagination runs instead to all the possibilities of breaking
existing code if such a change to a fundamental class were made.

I've been amazed by how subtly disruptive, seemingly simple changes
like the result of Array#to_s between Ruby 1.8 and 1.9 can be
http://talklikeaduck.denhaven2.com/2009/10/27/its-the-little-things

It would be better I think to have a new class which acted like an
Array and did recursive =3D=3D=3D for the seemingly rare cases where this i=
s
needed. And I don't think that such a class need be part of the
standard library. That's the beauty of a language like Ruby,
programmers can extend it for themselves to fit THEIR purposes, which
often are different from MY purposes.

--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
C

Caleb Clausen

I've been amazed by how subtly disruptive, seemingly simple changes
like the result of Array#to_s between Ruby 1.8 and 1.9 can be
http://talklikeaduck.denhaven2.com/2009/10/27/its-the-little-things

I added this to my tests for 1.9 porting. It at least makes the
Array#to_s problems readily apparent. Not intended as a permanent
measure.

if defined? ::RUBY_VERSION and ::RUBY_VERSION[/^\d+.\d+/].to_f>=1.9
class ::Array
alias to_s_without_semantics_changed_warning to_s

def to_s
warn "calling Array#to_s from #{caller.first}; semantics have changed"
to_s_without_semantics_changed_warning
end
end
end
 
B

Benoit Daloze

[Note: parts of this message were removed to make it a legal post.]

2009/12/19 Gary Wright said:
I still don't understand that particular change. The 1.8 semantics of
Array#to_s were much more useful in my mind than the 1.9 semantics.


Gary Wright
I don't think so, it became quite a mess when you did:
Values: [1, :b, "cd"]
1.8: Values: 1bcd
1.9: Values: [1, :b, "cd"]
I think the second shows better it's an Array.

Clearly Array#to_s was a (useful) shortcut to Array#join.

But I think that wasn't a way to do what you wanted. #to_s is made to show
the result in a String, usually on screen. It's quite different than wanting
to join every element with the following, what is the sense of #join.
 
G

Gary Wright

I don't think so, it became quite a mess when you did:
Values: [1, :b, "cd"]
1.8: Values: 1bcd
1.9: Values: [1, :b, "cd"]
I think the second shows better it's an Array.

A) making an incompatible change to something so basic as Array#to_s =
is/was a bad idea
B) Array#inspect was already available for debugging output
C) Array#to_s in 1.8 was useful for converting tree structures (nested =
arrays) to strings (in-order traversal).
But I think that wasn't a way to do what you wanted. #to_s is made to = show
the result in a String, usually on screen. It's quite different than = wanting
to join every element with the following, what is the sense of #join.

It seems intuitive to me that the string representation of a list would =
simply be the concatenation of the string representation of the =
individual elements. If I wanted extra punctuation to aid in debugging =
then Array#inspect was always available and if I wanted to include =
separators of some sort I had Array#join. The advantage of Array#to_s vs =
Array#join (for null separators) is that it integrates quite nicely with =
string interpolation as well as many of the IO output primitives.
 
W

Walton Hoops

-----Original Message-----
From: Tech W. [mailto:[email protected]]

Hello,

Does ruby have a DESTROY method in its class? like,

class Myclass
def DESTROY
something
end
end


Thanks.

No, objects in Ruby are simply garbage collected
when they go out of scope. It is never neccesary to explicitly
destroy an object. If there is any work you need done
before an object goes away, you had best do it before you let
it out of scope.
 
C

Christopher Dicely

Hello,

Does ruby have a DESTROY method in its class? like,

class Myclass
=C2=A0 def DESTROY
=C2=A0 =C2=A0 =C2=A0something
=C2=A0 end
end

There isn't a "DESTROY" (or "destroy") method defined by default. They
are legal names for methods you define (though DESTROY conventionally
would be a constant, not a method.)

You may be looking for C++-style destructors, which given the
difference in memory management and object lifecycle between Ruby and
C++ aren't really present in Ruby.

You'd probably get more useful answers asking for what you want to DO
rather than looking for a method name.
 
S

Seebs

for example, I openned a database handler in a class, and new an object
from this class, when the object go out of the scope, I want it close
the database handler automatically. so how to implement this in Ruby?
thanks again.

You don't. When you're done, you close it. More likely, you build your
class so that you would do something like:

foo.database_get do |db|
db.do_this
db.do_that
end

and database_get closes the database handle when it's done.

-s
 
R

Robert Klemme

2009/12/21 Tech W. said:
for example, I openned a database handler in a class, and =A0new an objec= t
from this class, when the object go out of the scope, I want it close the
database handler automatically. so how to implement this in Ruby? thanks
again.

There is keyword "ensure". You can also use blocks for this like
File.open does. You can read up on this here:
http://blog.rubybestpractices.com/posts/rklemme/002_Writing_Block_Methods.h=
tml

Kind regards

robert

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

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

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top