Some questions on language syntax

Hi, I'm new to this language and as I'm Perl user, some things seems strange to me:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for {'a' => 'b'}? Or, can I transform an array to some "list"? I can use Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list, only an array.

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want compare a and b accordind to some my own rules, i.e. if a[1] == b[2], "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

= can I somehow make ruby produce warnings on 1 == '1' (number == string) like comparisons? In Perl true, in Ruby false. Many my mistakes are of this kind and as these values seems same on output. :wink:

= why I can use {|...| ...} as argument for map, each etc., but I cannot write foo = {|...| ...}, though I can write bar = [...] or bar = {...}?

Thanks,

P.

Pavel Smerk scribbled on Tuesday 14 Mar 2006 21:50:

Hi, I'm new to this language and as I'm Perl user, some things seems
strange to me:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for
{'a' => 'b'}? Or, can I transform an array to some "list"? I can use
Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
only an array.

You can, however, expand the array:
Hash[*%w{a b c d}] yields {"a"=>"b", "c"=>"d"}

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
"return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

irb(main):006:0> 0 == true
=> false

0 is not true :slight_smile:

= can I somehow make ruby produce warnings on 1 == '1' (number ==
string) like comparisons? In Perl true, in Ruby false. Many my mistakes
are of this kind and as these values seems same on output. :wink:

Not that I know of. Well, you could extend the == operator on Fixnum (and
String) to throw a warning:

class Fixnum
  def ==(a)
    warn "Warn" unless a.is_a? Fixnum
    super(a)
  end
end

= why I can use {|...| ...} as argument for map, each etc., but I cannot
write foo = {|...| ...}, though I can write bar = [...] or bar = {...}?

You can't use {} literals in an assignment to denote a block because Ruby
thinks it is supposed to be a Hash. Use Kernel#proc (or the alias #lambda)
for that:

irb(main):026:0> p = proc {|n| puts n}
=> #<Proc:0xb7c98138@(irb):26>
irb(main):027:0> p.call(5)
5

You can pass that block to any method that wants a block (note the &
operator that denotes the passed argument is a block):

irb(main):029:0> [33, 44, 55].each &p
33
44
55

See also Method#block_given?

Hope that helps.

Hi, I'm new to this language and as I'm Perl user, some things seems strange to me:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for {'a' => 'b'}? Or, can I transform an array to some "list"? I can use Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list, only an array.

You are looking for the "splat" operator:

>> Hash[*%w{a b}]
=> {"a"=>"b"}

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want compare a and b accordind to some my own rules, i.e. if a[1] == b[2], "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

We use sort_by() for that:

>> %w{one two three}.sort_by { |str| [-str.length, str] }
=> ["three", "one", "two"]

= can I somehow make ruby produce warnings on 1 == '1' (number == string) like comparisons? In Perl true, in Ruby false. Many my mistakes are of this kind and as these values seems same on output. :wink:

Hmm, you could redefine ==(), but you don't want to do that, trust me. :wink: The transition phase will pass in time...

= why I can use {|...| ...} as argument for map, each etc., but I cannot write foo = {|...| ...}, though I can write bar = [...] or bar = {...}?

You can use lambda() for this:

proc_object = lambda { |...| ... }

James Edward Gray II

···

On Mar 14, 2006, at 3:03 PM, Pavel Smerk wrote:

Pavel Smerk wrote:

Hi, I'm new to this language and as I'm Perl user, some things seems
strange to me:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for
{'a' => 'b'}? Or, can I transform an array to some "list"? I can use
Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
only an array.

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
"return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

use #sort_by like:
Enumerable#sort_by { |obj| obj.fld1, obj.fld2 }

this list give good, quick, *accurate* answers, no?

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want compare a and b accordind to some my own rules, i.e. if a[1] == b[2], "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

because in ruby you don't have too:

   a <=> b

when a and b are arrays just does that. if you wanted to compare only the
first elements (assuming there are more) you can simply

   a[0,2] <=> b[0,2]

or, if you a glutten for perlishment

   [a[1], a[2]] <=> [b[0], b[1]]

you can do __really__ compact sorting routines in ruby. i wrote one yesterday
that sorts strings like this

   /dmsp/nrt/data/incoming/afwa/2006.f13_0731556_DT.DAT
   /dmsp/nrt/data/incoming/afwa/2006.f13_0731737_DS.DAT

where

   2006.f13_0731556_DT.DAT
   ^^^^ ^^^ ^^^^^^^ ^^
   > > > >
   year sat time type

first by sat, then by year, then by time and finally by type

using only

   pats = %w[ f\d\d ^\d{4} _\d{7} _\w{2} ].map{|p|/#{p}/}

   basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat| b[pat]} }

gotta love that!!

cheers.

-a

···

On Wed, 15 Mar 2006, Pavel Smerk wrote:
--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
compare a and b accordind to some my own rules, i.e. if a[1] == b
[2], "return" a[2] <=> b[2]? In Ruby this is not possible, because
0 is true.

James Gray wrote:

We use sort_by() for that:

Or nonzero? ... eg.

    (a[1] <=> b[2]).nonzero? || (a[2] <=> b[2])

···

On Mar 14, 2006, at 3:03 PM, Pavel Smerk wrote:

--
-- Jim Weirich

--
Posted via http://www.ruby-forum.com/\.

Bernhard 'elven' Stoeckner wrote:

Pavel Smerk scribbled on Tuesday 14 Mar 2006 21:50:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for
{'a' => 'b'}? Or, can I transform an array to some "list"? I can use
Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
only an array.

You can, however, expand the array:
Hash[*%w{a b c d}] yields {"a"=>"b", "c"=>"d"}

Oh, that's great!!! How could I not notice that? :wink:

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
"return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

irb(main):006:0> 0 == true
=> false

0 is not true :slight_smile:

Well, to be more precise, 0 is not equal to false (as is in Perl). :slight_smile:

So, how do you do a[1] <=> b[1] || a[2] <=> b[2]? Or, may be a.x <=> b.y

a.w <=> b.z would be better --- I don't want to suppose anything

about the internal structure of a and b now. However, Jim Weirich's
(a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK (and it's even in RDoc for Numeric#nonzero? ;-).

Gene Tani wrote:
> this list give good, quick, *accurate* answers, no?

:wink: Must agree with this. Thank for all suggestions, they are very valuable for me.

P.

Interesting idea, but may be _\w{2}\. would be more appropriate instead of _\w{2} which can match also against a beginnig of the 'time' part. :slight_smile:

P.

···

ara.t.howard@noaa.gov wrote:

that sorts strings like this

  /dmsp/nrt/data/incoming/afwa/2006.f13_0731556_DT.DAT
  /dmsp/nrt/data/incoming/afwa/2006.f13_0731737_DS.DAT

where

  2006.f13_0731556_DT.DAT
  ^^^^ ^^^ ^^^^^^^ ^^
  > > > >
  year sat time type

first by sat, then by year, then by time and finally by type

using only

  pats = %w[ f\d\d ^\d{4} _\d{7} _\w{2} ].map{|p|/#{p}/}

  basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat| b[pat]} }

Tangentially speaking you can use unpack and sort_by to do this kind of thing if it suits the situation (admittedly there's not a sort_by!, but let's not let that get in the way :slight_smile:

sorted_basenames = basenames.sort_by { |name|
   name.unpack('A4 x A3 x A7 x A2').values_at(1, 0, 2, 3)
}

or the delightful

sorted_basenames = basenames.sort_by { |name| name.unpack('x5 A3 X8 A4 x5 A7 x A2') }

This is just an excuse to expose unpack and sort_by to people, not a suggestion that it's necessarily appropriate or better in this case.

Mike

···

On 14-Mar-06, at 5:03 PM, ara.t.howard@noaa.gov wrote:

On Wed, 15 Mar 2006, Pavel Smerk wrote:

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want compare a and b accordind to some my own rules, i.e. if a[1] == b[2], "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

because in ruby you don't have too:

  a <=> b

when a and b are arrays just does that. if you wanted to compare only the
first elements (assuming there are more) you can simply

  a[0,2] <=> b[0,2]

or, if you a glutten for perlishment

  [a[1], a[2]] <=> [b[0], b[1]]

you can do __really__ compact sorting routines in ruby. i wrote one yesterday
that sorts strings like this

  /dmsp/nrt/data/incoming/afwa/2006.f13_0731556_DT.DAT
  /dmsp/nrt/data/incoming/afwa/2006.f13_0731737_DS.DAT

where

  2006.f13_0731556_DT.DAT
  ^^^^ ^^^ ^^^^^^^ ^^
  > > > >
  year sat time type

first by sat, then by year, then by time and finally by type

using only

  pats = %w[ f\d\d ^\d{4} _\d{7} _\w{2} ].map{|p|/#{p}/}

  basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat| b[pat]} }

gotta love that!!

--

Mike Stok <mike@stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.

I don't think that actually does what you want. The test should
return -1 if a[1] is less than b[2], but instead it will return
"true".

···

On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel Smerk wrote:

However, Jim Weirich's (a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK

right you are! bug number 63 squashed.

thanks!

-a

···

On Wed, 15 Mar 2006, Pavel Smerk wrote:

ara.t.howard@noaa.gov wrote:

that sorts strings like this

  /dmsp/nrt/data/incoming/afwa/2006.f13_0731556_DT.DAT
  /dmsp/nrt/data/incoming/afwa/2006.f13_0731737_DS.DAT

where

  2006.f13_0731556_DT.DAT
  ^^^^ ^^^ ^^^^^^^ ^^
  > > > >
  year sat time type

first by sat, then by year, then by time and finally by type

using only

  pats = %w[ f\d\d ^\d{4} _\d{7} _\w{2} ].map{|p|/#{p}/}

  basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat| b[pat]} }

Interesting idea, but may be _\w{2}\. would be more appropriate instead of _\w{2} which can match also against a beginnig of the 'time' part. :slight_smile:

P.

--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama

i actually like that. whether regexes or pack codes are more obtuse is
definitely debatable - but that's pretty clean looking

thanks for the tip.

-a

···

On Wed, 15 Mar 2006, Mike Stok wrote:

Tangentially speaking you can use unpack and sort_by to do this kind of thing if it suits the situation (admittedly there's not a sort_by!, but let's not let that get in the way :slight_smile:

sorted_basenames = basenames.sort_by { |name|
name.unpack('A4 x A3 x A7 x A2').values_at(1, 0, 2, 3)
}

or the delightful

sorted_basenames = basenames.sort_by { |name| name.unpack('x5 A3 X8 A4 x5 A7 x A2') }

This is just an excuse to expose unpack and sort_by to people, not a suggestion that it's necessarily appropriate or better in this case.

--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama

Are you sure?
------------------------------------------------------- Numeric#nonzero?
      num.nonzero? => num or nil

···

On 14-Mar-06, at 5:28 PM, Edward Faulkner wrote:

On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel Smerk wrote:

However, Jim Weirich's (a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK

I don't think that actually does what you want. The test should
return -1 if a[1] is less than b[2], but instead it will return
"true".

------------------------------------------------------------------------
      Returns _num_ if _num_ is not zero, +nil+ otherwise. This behavior
      is useful when chaining comparisons:

         a = %w( z Bb bB bb BB a aA Aa AA A )
         b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
         b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]

Mike

--

Mike Stok <mike@stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.

Edward Faulkner wrote:

However, Jim Weirich's (a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK

I don't think that actually does what you want. The test should
return -1 if a[1] is less than b[2], but instead it will return
"true".

No, it works:

(0 <=> 1).nonzero?

=> -1

It was for this exact purpose that nonzero? was introduced.

BTW, I hope no one was confused by the typo ... the first b[2] should
have been a b[1].

-- Jim Weirich

···

On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel Smerk wrote:

--
Posted via http://www.ruby-forum.com/\.