1

I was trying to solve a coding puzzle: Take strings, and count the ones that don't include repeated words.

This code works:

def validate(passphrase)
  words = passphrase.split
  words == words.uniq
end

passphrases = File.readlines("../input/passphrases.txt")

p passphrases.count {|phrase| validate(phrase)}
#=> 337

If I make one minor change with the count block, it counts all of the passphrases instead of just the ones that would return true when passed through the block:

p passphrases.count do |phrase| 
  validate(phrase)
end
#=>512

What's up with this?

sawa
  • 165,429
  • 45
  • 277
  • 381
Fluffy Ribbit
  • 316
  • 1
  • 4
  • 15
  • 2
    @sawa, the question you reference asks whether `do...end` or `{...}` should be used, generally, which is different than this question, but the selected answer to the referenced question is indeed the answer to this question. Does that make this a dup of the earlier question? Interesting. – Cary Swoveland Dec 05 '17 at 05:08

1 Answers1

3

It is due to argument precedence with the do block.

Your first example is equivalent to:

p(passphrases.count {|phrase| validate(phrase)})

The second one with the do block is equivalent to:

p(passphrases.count) do |phrase| 
  validate(phrase)
end

Where the do is being applied to the p function.

If you want the second case to match the first, wrap the whole block in parenthesis:

p(passphrases.count do |phrase| 
  validate(phrase)
end)
AbM
  • 7,326
  • 2
  • 25
  • 28
  • So, why doesn't calling p with a block not throw an error and also not seem to do anything? – Fluffy Ribbit Dec 05 '17 at 03:44
  • 1
    @FluffyRibbit Why would it throw an error? You can call any method with a block, it is up to the method to use the block if it wants it. Unfortunately, there is no way for a method to say that it requires a block or doesn't want one. – mu is too short Dec 05 '17 at 04:07
  • 1
    @muistooshort Can't a method say that with `raise "I require a block" unless block_given?` and `raise "I don't want a block" if block_given?`? – Stefan Pochmann Dec 05 '17 at 10:33
  • 1
    @StefanPochmann It can but that's very different than saying `def m(x)` to indicate that one parameter is required, or `def m(x:)` to require a keyword argument, or some hypothetical `def m(something_something_I_require_a_block)`, ... Taking a block is by convention rather than part of the method signature. You can do it by hand but no one does so it isn't very Rubyish (a word which gets autocorrected to "Rubbish", ha ha). Ruby has some odd ideas about how you should do things. – mu is too short Dec 05 '17 at 18:27