Why Don't Numbers Support .Dup

Why don't numbers support .dup?

Most objects in Ruby are passed by reference and can be dupped. Eg:

s = "Hello"
t = s # s & t reference to the same string
t.upcase! # modifying either one will affect the other
s # ==> "HELLO"

A few objects in Ruby are immediate, though. They are passed by value, there can only be one of this value and it therefore cannot be duped. These are any (small) integers, true, false, symbols and nil. Many floats are also immediates in Ruby 2.0 on 64 bit systems.

In this (preposterous) example, any "42" will hold the same instance variable.

class Fixnum
attr_accessor :name
alias_method :original_to_s, :to_s
def to_s
name || original_to_s
end
end
42.name = "The Answer"
puts *41..43 # => 41, The Answer, 43

Since you would normally expect something.dup.name = "new name" to not affect any other object than the copy obtained with dup, Ruby chooses not to define dup on immediates.

Your question is more complex than it appears. There was some discussion on ruby-core as to how this can be made easier. Also, other types of Numeric objects (floats, bignums, rationals and complex numbers) can not be duped although they are not immediates either.

Note that ActiveSupport (part of rails) provide the method duplicable? on all objects

Can someone explain what dup() in C does?

Just wanted to respond to myself on the second question after experimenting a bit.

The answer is YES. A file descriptor that you make can take a value 0, 1, 2 if stdin, stdout or stderr are closed.

Example:

close(1);     //closing stdout
newfd=dup(1); //newfd takes value of least available fd number

Where this happens to file descriptors:

0 stdin     .--------------.     0 stdin     .--------------.     0 stdin
1 stdout =| close(1) :=> 2 stderr =| newfd=dup(1) :=> 1 newfd
2 stderr '--------------' '--------------' 2 stderr

Duplicate a floating point number with Ruby in Sketchup

I can't see what your updated question has to do with the original one, but here's two ways to do what you wanted:

b = [a.dup, b.dup]
b[0][1] += 1.1
b[1][1] += 1.1

or

b.map! { |r| r.dup.tap { |r2| r2[1] += 1.1 } }

When to use dup, and when to use clone in Ruby?

It is true that clone copies the frozen state of an object, while dup does not:

o = Object.new
o.freeze

o.clone.frozen?
#=> true

o.dup.frozen?
#=> false

clone will also copy the singleton methods of the object while dup does not:

o = Object.new
def o.foo
42
end

o.clone.respond_to?(:foo)
#=> true

o.dup.respond_to?(:foo)
#=> false

Which leads me to the assumption that clone is sometimes understood as to provide a "deeper" copy than dup. Here are some quotes about the topic:

Comment on ActiveRecord::Base#initialize_dup from Rails 3:

Duped objects have no id assigned and are treated as new records. Note
that this is a "shallow" copy as it copies the object's attributes
only, not its associations. The extent of a "deep" copy is application
specific and is therefore left to the application to implement according
to its need.

An article about deep copies in Ruby:

There is another method worth mentioning, clone. The clone method does the same thing as dup with one important distinction: it's expected that objects will override this method with one that can do deep copies.

But then again, theres deep_dup in Rails 4:

Returns a deep copy of object if it's duplicable. If it's not duplicable, returns self.

and also ActiveRecord::Core#dup and #clone in Rails 4:

clone — Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied. [...] If you need a copy of your attributes hash, please use the #dup method.

Which means that here, the word dup is used to refer to a deep clone again. As far as I can see, there seems to be no consensus in the community, except that you should use clone and dup in the case when you need a specific side effect of either one.

Finally, I see dup much more often in Ruby code than clone. I have never used clone so far, and I won't until I explicitly need to.

Original variable still changes with .clone or .dup

A dup or clone in Ruby is a shallow clone, meaning that only the outer object is cloned, not its children. In your case, this means that the couples array is copied, but not each individual couple.

If you want it to be a deep clone, you need to do it manually for the stdlib arrays:

new_couples = original_couples.map { |couple| couple.clone }

If you are in a domain where copies of collections are often necessary, or you are trying to work in a more functional style, I suggest you take a look at the Hamster gem, that brings immutable data structures to ruby.

Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

There are at least 6 (!) ways to clone an array:

  • loop
  • slice
  • Array.from()
  • concat
  • spread operator (FASTEST)
  • map A.map(function(e){return e;});

There has been a huuuge BENCHMARKS thread, providing following information:

  • for blink browsers slice() is the fastest method, concat() is a bit slower, and while loop is 2.4x slower.

  • for other browsers while loop is the fastest method, since those browsers don't have internal optimizations for slice and concat.

This remains true in Jul 2016.

Below are simple scripts that you can copy-paste into your browser's console and run several times to see the picture. They output milliseconds, lower is better.

while loop

n = 1000*1000;
start = + new Date();
a = Array(n);
b = Array(n);
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

slice

n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);

Please note that these methods will clone the Array object itself, array contents however are copied by reference and are not deep cloned.

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true

Methods to create deep copy of objects without the help of Marshal

This solution works

class CashRegister
attr_accessor :bills

def initialize
@bills = []
end

def clone
cloned = super
cloned.bills = @bills.map { |bill| bill.clone }
cloned
end
end

class Bill
attr_accessor :positions

def initialize(nr)
@nr = nr
@positions = []
end

def clone
cloned = super
cloned.positions = @positions.map{ |pos| pos.clone }
cloned
end
end

class Position
attr_reader :price
attr_writer :product

# this method is given
def product
@product.clone
end

def initialize(product, price)
@product = product
@price = price
end

def clone
cloned = super
cloned.product = product
cloned
end
end

Use [].replace to make a copy of an array

This is the tricky concept of mutability in ruby. In terms of core objects, this usually comes up with arrays and hashes. Strings are mutable as well, but this can be disabled with a flag at the top of the script. See What does the comment "frozen_string_literal: true" do?.

In this case, you can call dup, deep_dup, clone easily to the same effect as replace:

['some', 'array'].dup
['some', 'array'].deep_dup
['some', 'array'].clone
Marshal.load Marshal::dump(['some', 'array'])

In terms of differences, dup and clone are the same except for some nuanced details - see What's the difference between Ruby's dup and clone methods?

The difference between these and deep_dup is that deep_dup works recursively. For example if you dup a nested array, the inner array will not be cloned:

  a = [[1]]
b = a.clone
b[0][0] = 2
a # => [[2]]

The same thing happens with hashes.

Marshal.load Marshal::dump <object> is a general approach to deep cloning objects, which, unlike deep_dup, is in ruby core. Marshal::dump returns a string so it can be handy in serializing objects to file.

If you want to avoid unexpected errors like this, keep a mental index of which methods have side-effects and only call those when it makes sense to. An explanation point at the end of a method name indicates that it has side effects, but others include unshift, push, concat, delete, and pop. A big part of fuctional programming is avoiding side effects. You can see https://www.sitepoint.com/functional-programming-techniques-with-ruby-part-i/

lodash: Get duplicate values from an array

You can use this:

_.filter(arr, (val, i, iteratee) => _.includes(iteratee, val, i + 1))

Note that if a number appears more than two times in your array you can always use _.uniq.



Related Topics



Leave a reply



Submit