Project

General

Profile

Feature #8987

map/collect extension which handles arguments

Added by So Wieso about 3 years ago. Updated over 2 years ago.

Status:
Open
Priority:
Normal
Assignee:
-
[ruby-core:57679]

Description

Please consider extending map/collect by allowing additional arguments to be passed to proc, like:
A: [1,2,3,4].map :+, 4
and/or
B: [1,2,3,4].map 4, &:+

=> [5, 6, 7, 8]

Variant A is probably more readable. Variant B is more versatile (allows arbitrary arguments to be passed to block).

mappi.rb View - sample implementation in ruby (called mappi) (410 Bytes) So Wieso, 10/05/2013 11:02 PM


Related issues

Related to Ruby trunk - Feature #4146: Improvement of Symbol and Proc Rejected

History

#1 [ruby-core:57680] Updated by Tsuyoshi Sawada about 3 years ago

In case of commutative operations like +, you can do it like this:

[1, 2, 3, 4].map(&4.method(:+))
# => [5, 6, 7, 8]

#2 [ruby-core:60244] Updated by Hiroshi SHIBATA almost 3 years ago

  • Target version changed from 2.1.0 to current: 2.2.0

#3 [ruby-core:60370] Updated by Koichi Sasada almost 3 years ago

(2013/10/06 0:26), sawa (Tsuyoshi Sawada) wrote:

[1, 2, 3, 4].map(&4.method(:+))
# => [5, 6, 7, 8]

Interesting.

If we use λ (alias of lambda), it is more short.

module Kernel
  alias λ lambda
end
p [1, 2, 3, 4].map(&4.method(:+)) #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{|x| 4+x})  #=> [5, 6, 7, 8]

If we define λ as the following definition, more short code.

module Kernel
  def λ(a, sym)
    lambda{|x| a.send(sym, x)}
  end
end

p [1, 2, 3, 4].map(&λ(4, :+))  #=> [5, 6, 7, 8]

A bit shorter version.

module Kernel
  def λ(expr)
    eval("lambda{|x| #{expr} x}")
  end
end

p [1, 2, 3, 4].map(&λ("4+"))  #=> [5, 6, 7, 8]

If we have default parameter `_' (maybe matz doesn't like), we can make
more short code.

p [1, 2, 3, 4].map(&λ{4+_})      #=> [5, 6, 7, 8]

Summary:

p [1, 2, 3, 4].map(&4.method(:+)) #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{|x| 4+x})  #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ(4, :+))    #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ("4+"))     #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{4+_})      #=> [5, 6, 7, 8] (doesn't run)

--
// SASADA Koichi at atdot dot net

#4 [ruby-core:60372] Updated by Matthew Kerwin almost 3 years ago

On 31 January 2014 15:48, SASADA Koichi ko1@atdot.net wrote:

p [1, 2, 3, 4].map(&4.method(:+)) #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{|x| 4+x})  #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ(4, :+))    #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ("4+"))     #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{4+_})      #=> [5, 6, 7, 8] (doesn't run)

Are any of these actually better than:

p [1, 2, 3, 4].map{|x| 4+x }

?

--
Matthew Kerwin
http://matthew.kerwin.net.au/

#5 [ruby-core:60374] Updated by Koichi Sasada almost 3 years ago

Matthew Kerwin wrote:

Are any of these actually better than:

 p [1, 2, 3, 4].map{|x| 4+x }

?

LOL

#6 [ruby-core:60381] Updated by So Wieso almost 3 years ago

Matthew Kerwin wrote:

On 31 January 2014 15:48, SASADA Koichi ko1@atdot.net wrote:

p [1, 2, 3, 4].map(&4.method(:+)) #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{|x| 4+x})  #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ(4, :+))    #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ("4+"))     #=> [5, 6, 7, 8]
p [1, 2, 3, 4].map(&λ{4+_})      #=> [5, 6, 7, 8] (doesn't run)

Are any of these actually better than:

 p [1, 2, 3, 4].map{|x| 4+x }

?

--
Matthew Kerwin
http://matthew.kerwin.net.au/

Actually I believe the most readable form would be

p [1,2,3,4].map{ 4 + _ }  # when there is no |…|, set block params to _ 

Many blocks in realworld-code are so easy, that it is really a barrier to have to think about a name, and therefore one uses often non-verbose names like you used x. Where is the point in being forced to think of a name, when you don't set the name to something meaningful. I guess this was the idea why the to_proc convention was introduced. The problem with it is, that it is really limiting because you cannot use parameters (and the presence of a parameter doesn't necessarily make problems so complex to justify a name).

#7 [ruby-core:60388] Updated by Yusuke Endoh almost 3 years ago

Just joke.

p [0,1,2,3].dmap + 1 #=> [1, 2, 3, 4]

# %w(foo bar baz).map {|x| x.upcase.concat("!") }
p %w(foo bar baz).map_do.upcase.concat("!").end
  # => ["FOO!", "BAR!", "BAZ!"]

Source:

class DelegateMap < BasicObject
  def initialize(enum)
    @enum = enum
  end
  def method_missing(mhd, *args, &blk)
    @enum.map {|elem| elem.__send__(mhd, *args, &blk) }
  end
end
class CascadingDelegateMap < BasicObject
  def initialize(enum)
    @enum = enum
  end
  def method_missing(mhd, *args)
    ::CascadingDelegateMap.new(@enum.map {|elem| elem.send(mhd, *args) })
  end
  def end
    @enum
  end
end
module Enumerable
  def dmap
    DelegateMap.new(self)
  end
  def map_do
    CascadingDelegateMap.new(self)
  end
end

--
Yusuke Endoh mame@tsg.ne.jp

#8 [ruby-core:60392] Updated by Matthew Kerwin almost 3 years ago

On Jan 31, 2014 6:20 PM, sowieso@dukun.de wrote:

Actually I believe the most readable form would be

p [1,2,3,4].map{ 4 + _ } # when there is no |...|, set block params to _

Many blocks in realworld-code are so easy, that it is really a barrier to
have to think about a name, and therefore one uses often non-verbose names
like you used x. Where is the point in being forced to think of a name,
when you don't set the name to something meaningful. I guess this was the
idea why the to_proc convention was introduced. The problem with it is,
that it is really limiting because you cannot use parameters (and the
presence of a parameter doesn't necessarily make problems so complex to
justify a name).

I guess you can solve it by syntax or by convention. I've, personally,
never had a pause when calling it 'x' or 'item', depending on the context.
You could also develop the convention of: arr.map{|_| ... }

My only problem with magic variables is that I can never remember when they
get (re)assigned. It's particularly annoying in perl, because those guys
never assign a variable when $_ will suffice.

Matthew Kerwin

#9 [ruby-core:60409] Updated by Tsuyoshi Sawada almost 3 years ago

Probably, it makes more sense to extend the syntax of Symbol#to_proc. The conventional Symbol#to_proc does not take an argument:

:foo.to_proc # => ->(x){x.foo}

My proposal is to let it take optional arguments that would be passed to the method within the created proc:

:foo.to_proc(y) # => ->(x){x.foo(y)}

So that

:+.to_proc(4) # => ->(x){x + 4}
[1, 2, 3, 4].map(&:+.to_proc(4)) # => [5, 6, 7, 8]

Not sure if any better than writing the original, but looks consistent.

Or, maybe we can use the method name Symbol#call, which is aliased to short forms, so that we can do:

:+.call(4) # => ->(x){x + 4}
:+.(4) # => ->(x){x + 4}
[1, 2, 3, 4].map(&:+.(4)) # => [5, 6, 7, 8]

#10 [ruby-core:60416] Updated by Henry Maddocks almost 3 years ago

Tsuyoshi Sawada wrote:

Probably, it makes more sense to extend the syntax of Symbol#to_proc. The conventional Symbol#to_proc does not take an argument:

+1

#11 [ruby-core:62954] Updated by Nobuyoshi Nakada over 2 years ago

#12 [ruby-core:62957] Updated by Nobuyoshi Nakada over 2 years ago

  • Description updated (diff)

Also available in: Atom PDF