Project

General

Profile

Actions

Feature #8987

open

map/collect extension which handles arguments

Added by sowieso (So Wieso) over 10 years ago. Updated about 6 years ago.

Status:
Open
Assignee:
-
Target version:
-
[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).


Files

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

Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #4146: Improvement of Symbol and ProcRejectednobu (Nobuyoshi Nakada)Actions

Updated by sawa (Tsuyoshi Sawada) over 10 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]

Updated by hsbt (Hiroshi SHIBATA) about 10 years ago

  • Target version changed from 2.1.0 to 2.2.0

Updated by ko1 (Koichi Sasada) about 10 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

Updated by phluid61 (Matthew Kerwin) about 10 years ago

On 31 January 2014 15:48, SASADA Koichi 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/

Updated by ko1 (Koichi Sasada) about 10 years ago

Matthew Kerwin wrote:

Are any of these actually better than:

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

?

LOL

Updated by sowieso (So Wieso) about 10 years ago

Matthew Kerwin wrote:

On 31 January 2014 15:48, SASADA Koichi 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).

Updated by mame (Yusuke Endoh) about 10 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

Updated by phluid61 (Matthew Kerwin) about 10 years ago

On Jan 31, 2014 6:20 PM, 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

Updated by sawa (Tsuyoshi Sawada) about 10 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]

Updated by henry.maddocks (Henry Maddocks) about 10 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

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

  • Description updated (diff)
Actions #13

Updated by naruse (Yui NARUSE) about 6 years ago

  • Target version deleted (2.2.0)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0