Unit testing an each function

J

James Britt

James said:
I believe James Britt said he had a copy of the docs but couldn't see
the value in putting them up:

Well, no. What I said was, "There had been an older version on them on
ruby-doc, though I think they got lost in the server shuffle.

I can add them again, if there is sufficient interest. But it seems
that they're a bit avant garde for most people who wouldn't already have
them locally anyway."

*I* can see the value, but I wasn't sure who else had a need for them
that didn't already have a more convenient local copy.

And they *used* to be up but got lost in a menu re-org or something or
other.
I filed a complaint, maybe you should add one too. :(

OK, that frowny face really put me over the edge, so:

http://ruby-doc.org/core-1.9/

I'll add a link to the main page later on when I get home.

James Britt


--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
 
J

James Edward Gray II

James Edward Gray II wrote:



Do you think what I built here is worth packaging up into a gem?

Hmm, isn't what you wrote an automatic Generator class? (See
standard library.)

Yeah, I don't have a problem with it. I'm probably not the kind of
guy who would using it, but if you and others would, go for it. I
love sharing. :)

James Edward Gray II
 
K

Kevin Ballard

James said:
Hmm, isn't what you wrote an automatic Generator class? (See
standard library.)

Not really. Generator basically gives you external iterators which you
can access element by element, so you can do things like iterate over 2
enumerables in step. What I wrote is basically just a convenience for
applying a method call to all the elements in a single enumerable.
After all,

%w{a long list of words}.collect.length

is the same thing as

%w{a long list of words}.collect { |item| item.length}

but easier to read.
Yeah, I don't have a problem with it. I'm probably not the kind of
guy who would using it, but if you and others would, go for it. I
love sharing. :)

Ok, then I'll probably package it up tomorrow.
 
E

ES

Kevin said:
Not really. Generator basically gives you external iterators which you
can access element by element, so you can do things like iterate over 2
enumerables in step. What I wrote is basically just a convenience for
applying a method call to all the elements in a single enumerable.
After all,

%w{a long list of words}.collect.length

is the same thing as

%w{a long list of words}.collect { |item| item.length}

but easier to read.




Ok, then I'll probably package it up tomorrow.

Please do! This idiom is an improvement over dbrock's foo.each {.succ}
which still required a preprocessor.

E
 
E

ES

ES said:
Please do! This idiom is an improvement over dbrock's foo.each {.succ}
which still required a preprocessor.

Whoops, sent too soon! I was going to continue to say that while this is
nice, for clarity of expression you might want to state it this way:

%w{a long list of words}.collect :length

E
 
B

Brian Schröder

Not really. Generator basically gives you external iterators which you
can access element by element, so you can do things like iterate over 2
enumerables in step. What I wrote is basically just a convenience for
applying a method call to all the elements in a single enumerable.
After all,

%w{a long list of words}.collect.length

is the same thing as

%w{a long list of words}.collect { |item| item.length}

but easier to read.

I'd understand this
%w{a long list of words}.collect.length
as giving me the length of the return value of collect. Collect
returns an array so I'd expect the above to return five. Maybe it
would be better to write

%w{a long list of words}.collect_with :length

where in method missing something like this would happen

module Enumerable
def method_missing(method, *methods, &block)
if /(^.*)_with$/ =3D~ method.to_s
send($1) do | a |=09
=09methods.inject(a) { | r, m | r.send(m) }
end
else
super
end
end
end

%w{a long list of words}.collect_with :length, :to_s
# =3D> ["1", "4", "4", "2", "5"]

regards,

Brian
 
P

Pit Capitain

Brian said:
I'd understand this
=20
%w{a long list of words}.collect.length
=20
as giving me the length of the return value of collect. Collect
returns an array so I'd expect the above to return five. Maybe it
would be better to write
=20
%w{a long list of words}.collect_with :length
=20
where in method missing something like this would happen
=20
module Enumerable
def method_missing(method, *methods, &block)
if /(^.*)_with$/ =3D~ method.to_s
send($1) do | a |=09
methods.inject(a) { | r, m | r.send(m) }
end
else
super
end
end
end
=20
%w{a long list of words}.collect_with :length, :to_s
# =3D> ["1", "4", "4", "2", "5"]

With the famous

class Symbol
def to_proc
lambda { |obj| obj.send self }
end
end

you can get the same result via

%w{a long list of words}.map(&:length).map(&:to_s)

I'd bet the Symbol#to_proc is somewhere in Nano.

Regards,
Pit
 
B

Brian Schröder

Brian said:
I'd understand this
%w{a long list of words}.collect.length

as giving me the length of the return value of collect. Collect
returns an array so I'd expect the above to return five. Maybe it
would be better to write

%w{a long list of words}.collect_with :length

where in method missing something like this would happen

module Enumerable
def method_missing(method, *methods, &block)
if /(^.*)_with$/ =3D~ method.to_s
send($1) do | a |
methods.inject(a) { | r, m | r.send(m) }
end
else
super
end
end
end

%w{a long list of words}.collect_with :length, :to_s
# =3D> ["1", "4", "4", "2", "5"]

With the famous

class Symbol
def to_proc
lambda { |obj| obj.send self }
end
end

you can get the same result via

%w{a long list of words}.map(&:length).map(&:to_s)

I'd bet the Symbol#to_proc is somewhere in Nano.

Regards,
Pit

Thats interesting pit. Thank you. Though I'm quite shure I'd never use
either of the solutions proposed here, because I think they introduce
unreadability by uncommon magic. Better to write

%w{a long list of words}.map { | e | e.length.to_s }

which additionally has less line noise in it.

regards,

Brian
 
K

Kevin Ballard

ES said:
Whoops, sent too soon! I was going to continue >to say that while this is
nice, for clarity of expression you might want to >state it this way:

%w{a long list of words}.collect :length

But then that interferes with the ability to do

[(1..5), (10..15)].each.each { |i| puts i }

(which was the original point of this exercise) or any other nested
enumerations such as

[(1..5), (10..15)].collect.collect {|i| i + 1}
I'd understand this
%w{a long list of words}.collect.length
as giving me the length of the return value of collect. Collect
returns an array so I'd expect the above to return five. Maybe it
would be better to write

%w{a long list of words}.collect_with :length

where in method missing something like this would happen

[...]

Again, same problem, plus the fact that I don't want to be using
method_missing as it would interfere with any existing method_missing
With the famous

class Symbol
def to_proc
lambda { |obj| obj.send self }
end
end

you can get the same result via

%w{a long list of words}.map(&:length).map(&:to_s)

I'd bet the Symbol#to_proc is somewhere in Nano.

I find that much harder to read, and it also precludes the possibility
of something like

(1..100).reject.even?

This, of course, relies on a definition of Integer#even? and having the
reject method wrapped, but that's pretty easy:

class Integer
def even?
(self % 2).zero?
end
end
Enumerable::wrap_meth Enumerable, :reject

Granted, for a single use, you'd probably just want to do

(1..100).reject { |i| (i % 2).zero? }

rather than defining a new method, but this example is just for
illustration.

Of course, it's really intended for dealing with nested enumerables,
like the [(1..5), (10..15)] example above, but it does simplify very
simple single-enumerable handling.
 
B

Brian Schröder

[snip]
(1..100).reject.even?

[snip]

Just wanted to state that on second reading of this thread it again
did hurt in my eyes. If I read (1.100).reject I expect a resulting
array. And when I read the above I wondered shortly if even? works on
arrays maybe returning true if the array has an even number of
entries. Then I remembered the thread topic and started this text. So
a
-1
from me for proposing this as an addition to the language.

regards,

Brian
 
K

Kevin Ballard

Brian said:
[snip]

(1..100).reject.even?

[snip]

Just wanted to state that on second reading of this thread it again
did hurt in my eyes. If I read (1.100).reject I expect a resulting
array. And when I read the above I wondered shortly if even? works on
arrays maybe returning true if the array has an even number of
entries. Then I remembered the thread topic and started this text. So
a
-1
from me for proposing this as an addition to the language.

Except that (1..100).reject doesn't return an array at all unless a
block is supplied - it gives a LocalJumpError. All the enumerating
methods I've tested except for Enumerable#collect give an error when
called without a block (#collect without a block is equivalent to
#to_a). So with this change, the case where no block is supplied
becomes useful as opposed to useless (and in the #collect case, I can't
imagine anybody's actually relying on #collect being equivalent to
#to_a when called without a block, because they should be using #to_a
directly for that).

In any case, I wasn't proposing this as an addition to the language
(not that I would mind), I was simply proposing this as something I
should package up into a gem so people could use it if they wanted.
 
B

Brian Schröder

Brian said:
[snip]

(1..100).reject.even?

[snip]

Just wanted to state that on second reading of this thread it again
did hurt in my eyes. If I read (1.100).reject I expect a resulting
array. And when I read the above I wondered shortly if even? works on
arrays maybe returning true if the array has an even number of
entries. Then I remembered the thread topic and started this text. So
a
-1
from me for proposing this as an addition to the language.

Except that (1..100).reject doesn't return an array at all unless a
block is supplied - it gives a LocalJumpError. All the enumerating
methods I've tested except for Enumerable#collect give an error when
called without a block (#collect without a block is equivalent to
#to_a). So with this change, the case where no block is supplied
becomes useful as opposed to useless (and in the #collect case, I can't
imagine anybody's actually relying on #collect being equivalent to
#to_a when called without a block, because they should be using #to_a
directly for that).

In any case, I wasn't proposing this as an addition to the language
(not that I would mind), I was simply proposing this as something I
should package up into a gem so people could use it if they wanted.

Hello Kevin,

it is clear that reject without a block does not work, it was more a
question of visual parsing. Reject without a block has no meaning,
because we need to tell what to reject. And telling this is done with
a block. Your quite intelligent magic gives IMHO a wrong meaning to
the dot operator. You seem not to call .even? on the result of a
reject.

Well effectively you do, but I read chained methods as doing a chain
of succeding operations on a chain of objects, and that is not what I
read when I read (1..100).reject.even? Therefore I proposed
(1..100).reject :even?, because there it is made clearer that even is
given to reject to do something.

Nonetheless go ahed and package it as a gem. I just wanted to voice
what was disturbing me with this syntax.

Best regards,

Brian
 
K

Kevin Ballard

Brian said:
Hello Kevin,

it is clear that reject without a block does not work, it was more a
question of visual parsing. Reject without a block has no meaning,
because we need to tell what to reject. And telling this is done with
a block. Your quite intelligent magic gives IMHO a wrong meaning to
the dot operator. You seem not to call .even? on the result of a
reject.

Well effectively you do, but I read chained methods as doing a chain
of succeding operations on a chain of objects, and that is not what I
read when I read (1..100).reject.even? Therefore I proposed
(1..100).reject :even?, because there it is made clearer that even is
given to reject to do something.

Nonetheless go ahed and package it as a gem. I just wanted to voice
what was disturbing me with this syntax.

I understand your disturbance, but the syntax you propose makes the
primary use of this idiom impossible, which is

[(1..5),(10..15)].each.each { |i| puts i }

What would you suggest, making

[(1..5),(10..15)].each:)each) { |i| puts i }

behave that way? That looks even more wrong then what my code is doing.
And what if you have even further nesting?

[[(1..3), (5..8)], [(4..7), (2..6)]].each.each.each { |i| puts i }

Would that be

[[(1..3), (5..8)], [(4..7), (2..6)]].each:)each, :each) { |i| puts i
}

in your syntax? That's even *harder* to read.

Do you see any way to provide the proper behaviour without using the
syntax my code supports?
 
B

Brian Schröder

Brian said:
Hello Kevin,

it is clear that reject without a block does not work, it was more a
question of visual parsing. Reject without a block has no meaning,
because we need to tell what to reject. And telling this is done with
a block. Your quite intelligent magic gives IMHO a wrong meaning to
the dot operator. You seem not to call .even? on the result of a
reject.

Well effectively you do, but I read chained methods as doing a chain
of succeding operations on a chain of objects, and that is not what I
read when I read (1..100).reject.even? Therefore I proposed
(1..100).reject :even?, because there it is made clearer that even is
given to reject to do something.

Nonetheless go ahed and package it as a gem. I just wanted to voice
what was disturbing me with this syntax.

I understand your disturbance, but the syntax you propose makes the
primary use of this idiom impossible, which is

[(1..5),(10..15)].each.each { |i| puts i }

What would you suggest, making

[(1..5),(10..15)].each:)each) { |i| puts i }

behave that way? That looks even more wrong then what my code is doing.
And what if you have even further nesting?

[[(1..3), (5..8)], [(4..7), (2..6)]].each.each.each { |i| puts i }

Would that be

[[(1..3), (5..8)], [(4..7), (2..6)]].each:)each, :each) { |i| puts i
}

in your syntax? That's even *harder* to read.

Do you see any way to provide the proper behaviour without using the
syntax my code supports?

Sorry, I do not see a better syntax. But I have to admit that I have
no idea what would be the result of

[[(1..3), (5..8)], [(4..7), (2..6)]].each.each.each { |i| puts i }

(Well if I think long enough, maybe you mean something like

[[(1..3), (5..8)], [(4..7), (2..6)]].each do | row |
row.each do | range |
range.each do |i| puts i end
end
end

Which I would prefer about the "new" idiom, but other people may not.

regards,

Brian
 
K

Kevin Ballard

Brian said:
Sorry, I do not see a better syntax. But I have to admit that I have
no idea what would be the result of

[[(1..3), (5..8)], [(4..7), (2..6)]].each.each.each { |i| puts i }

(Well if I think long enough, maybe you mean something like

[[(1..3), (5..8)], [(4..7), (2..6)]].each do | row |
row.each do | range |
range.each do |i| puts i end
end
end

Which I would prefer about the "new" idiom, but other people may not.

Yes, that is what the construct means. Each successive call is one
deeper level of enumeration. If you understand what the nested
enumeration semantics mean, then it's not hard to read.
 
E

ES

Kevin said:
Brian Schr=F6der wrote:
=20
Sorry, I do not see a better syntax. But I have to admit that I have
no idea what would be the result of

[[(1..3), (5..8)], [(4..7), (2..6)]].each.each.each { |i| puts i }

This is *really* hard to read if you just see:

@some_array.each.each.each {|i| puts i}
(Well if I think long enough, maybe you mean something like

[[(1..3), (5..8)], [(4..7), (2..6)]].each do | row |
row.each do | range |
range.each do |i| puts i end
end
end

Which I would prefer about the "new" idiom, but other people may not.
=20
=20
Yes, that is what the construct means. Each successive call is one
deeper level of enumeration. If you understand what the nested
enumeration semantics mean, then it's not hard to read.

You are also happily ignoring the non-enumerable confusion that this
may cause. Of course, this being a library, you are free to do as you
please.

E
 

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
474,181
Messages
2,570,970
Members
47,537
Latest member
BellCorone

Latest Threads

Top