Polymorphic copy: Difference between revisions

From Rosetta Code
Content added Content deleted
m (whitespace)
(→‎{{header|Tcl}}: Implement an object cloning demonstration)
Line 518: Line 518:
Tcl values are logically immutable, and are passed around by reference with copies being taken as and when it is necessary to do so to maintain the immutable model. Hence an effective copy of any value is just:
Tcl values are logically immutable, and are passed around by reference with copies being taken as and when it is necessary to do so to maintain the immutable model. Hence an effective copy of any value is just:
<lang tcl>set varCopy $varOriginal</lang>
<lang tcl>set varCopy $varOriginal</lang>
With objects, slightly more work is required because they are normally passed around by name/reference.
<br>
{{works with|Tcl|8.6}}
<lang tcl>oo::class create CanClone {
method clone {{name {}}} {
# Make a bare, optionally named, copy of the object
set new [oo::copy [self] {*}[expr {$name eq "" ? {} : [list $name]}]]

# Reproduce the basic variable state of the object
set newns [info object namespace $new]
foreach v [info object vars [self]] {
namespace upvar [namespace current] $v v1
namespace upvar $newns $v v2
if {[array exists v1]} {
array set v2 [array get v1]
} else {
set v2 $v1
}
}
# Other object state is possible like open file handles. Cloning that is
# properly left to subclasses, of course.

return $new
}
}

# Now a demonstration
oo::class create Example {
superclass CanClone
variable Count Name
constructor {name} {set Name $name;set Count 0}
method step {} {incr Count;return}
method print {} {puts "this is $Name in [self], stepped $Count times"}
method rename {newName} {set Name $newName}
}
set obj1 [Example new "Abracadabra"]
$obj1 step
$obj1 step
$obj1 print
set obj2 [$obj1 clone]
$obj2 step
$obj2 print
$obj2 rename "Hocus Pocus"
$obj2 print
$obj1 print</lang>
Which produces this output (object names might vary if you run it):
<pre>
this is Abracadabra in ::oo::Obj5, stepped 2 times
this is Abracadabra in ::oo::Obj6, stepped 3 times
this is Hocus Pocus in ::oo::Obj6, stepped 3 times
this is Abracadabra in ::oo::Obj5, stepped 2 times
</pre>


{{Omit From|ALGOL 68}} <!-- it isn't immediately obvious that ALGOL 68 is object oriented -->
{{Omit From|ALGOL 68}} <!-- it isn't immediately obvious that ALGOL 68 is object oriented -->

Revision as of 08:53, 8 November 2009

Task
Polymorphic copy
You are encouraged to solve this task according to the task description, using any language you may know.

An object is polymorphic when its specific type may vary. The types a specific value may take, is called class.

It is trivial to copy an object if its type is known: <lang c>int x; int y = x;</lang> Here x is not polymorphic, so y is declared of same type (int) as x. But if the specific type of x were unknown, then y could not be declared of any specific type.

The task: let a polymorphic object contain an instance of some specific type S derived from a type T. The type T is known. The type S is possibly unknown until run time. The objective is to create an exact copy of such polymorphic object (not to create a reference, nor a pointer to). Let further the type T have a method overridden by S. This method is to be called on the copy to demonstrate that the specific type of the copy is indeed S.

Ada

<lang ada>with Ada.Text_IO; use Ada.Text_IO;

procedure Test_Polymorphic_Copy is

  package Base is
     type T is tagged null record;
     function Name (X : T) return String;
  end Base;
  use Base;
  
  package body Base is
     function Name (X : T) return String is
     begin
        return "T";
     end Name;
  end Base;
  
     -- The procedure knows nothing about S
  procedure Copier (X : T'Class) is
     Duplicate : T'Class := X;  -- A copy of X
  begin
     Put_Line ("Copied " & Duplicate.Name); -- Check the copy
  end Copier;
  package Derived is   
     type S is new T with null record;
     overriding function Name (X : S) return String;
  end Derived;
  use Derived;
  
  package body Derived is   
     function Name (X : S) return String is
     begin
        return "S";
     end Name;
  end Derived;
  Object_1 : T;
  Object_2 : S;

begin

  Copier (Object_1);
  Copier (Object_2);

end Test_Polymorphic_Copy;</lang> The procedure Copier does not know the specific type of its argument. Nevertheless it creates an object Duplicate of exactly same type. Sample output:

Copied T
Copied S

C++

<lang cpp>#include <iostream>

class T { public:

 virtual void identify() { std::cout << "I am a genuine T" << std::endl; }
 virtual T* clone() { return new T(*this); }
 virtual ~T() {}

};

class S: public T { public:

 virtual void identify() { std::cout << "I am an S" << std::endl; }
 virtual S* clone() { return new S(*this); }

};

class X // the class of the object which contains a T or S { public:

 // by getting the object through a pointer to T, X cannot know if it's an S or a T
 X(T* t): member(t) {}
 // copy constructor
 X(X const& other): member(other.member->clone()) {}
 // copy assignment operator
 X& operator=(X const& other)
 {
   T* new_member = other.member->clone();
   delete member;
   member = new_member;
 }
 // destructor
 ~X() { delete member; }
 // check what sort of object it contains
 void identify_member() { member->identify(); }

private:

 T* member;

};

int main() {

 X original(new S);      // construct an X and give it an S,
 X copy = original;      // copy it,
 copy.identify_member(); // and check what type of member it contains

}</lang>

Common Lisp

With Structures

With structures, copy-structure performs the right kind of copy. The object and its copy are compared under eq, eql, equal, and equalp to demonstrate that "The objective is to create an exact copy of such polymorphic object (not to create a reference, nor a pointer to)."

<lang lisp>(defstruct super foo)

(defstruct (sub (:include super)) bar)

(defgeneric frob (thing))

(defmethod frob ((super super))

 (format t "~&Super has foo = ~w." (super-foo super)))

(defmethod frob ((sub sub))

 (format t "~&Sub has foo = ~w, bar = ~w."
         (sub-foo sub) (sub-bar sub)))</lang>
> (let* ((sub1 (make-sub :foo 'foo :bar 'bar))
         (sub2 (copy-structure sub1)))
    (frob sub1)
    (frob sub2)
    (format t "~&eq: ~w;  eql: ~w;  equal: ~w;  equalp: ~w"
            (eq sub1 sub2) (eql sub1 sub2)
            (equal sub1 sub2) (equalp sub1 sub2)))
Sub has foo = FOO, bar = BAR.
Sub has foo = FOO, bar = BAR.
eq: NIL;  eql: NIL;  equal: NIL;  equalp: T
NIL

With Sequences

The same technique works for sequence and its subclasses (e.g., string, list) when copy-seq is used rather than copy-structure.

<lang lisp>(defmethod frob ((sequence sequence))

 (format t "~&sequence has ~w elements" (length sequence)))

(defmethod frob ((string string))

 (format t "~&the string has ~w elements" (length string)))</lang>
> (let* ((hw1 "hello world")
         (hw2 (copy-seq hw1)))
    (frob hw1)
    (frob hw2)
    (format t "~&eq: ~w;  eql: ~w;  equal: ~w;  equalp: ~w"
            (eq hw1 hw2) (eql hw1 hw2)
            (equal hw1 hw2) (equalp hw1 hw2)))
the string has 11 elements
the string has 11 elements
eq: NIL;  eql: NIL;  equal: T;  equalp: T
NIL

D

Works with: Tango

If we assume there are no data members, this will be quite short and simple: <lang D>import tango.io.Stdout;

class T {

   char[] toString() { return "I'm the instance of T"; }
   T duplicate() { return new T; }

}

class S : T {

   char[] toString() { return "I'm the instance of S"; }
   override
   T duplicate() { return new S; }

}

void main () {

   T orig = new S;
   T copy = orig.duplicate;

   Stdout (orig).newline;
   Stdout (copy).newline;

}</lang>

Hovever this doesn't happen often in reality.

If we want to copy data fields we should have something like copy ctor, that will do the deep copy. <lang D>import tango.io.Stdout;

class T {

   this(T t = null) { } // ctor that will be used for copying
   char[] toString() { return "I'm the instance of T"; }
   T duplicate() { return new T(this); }
   int custom(int x) { return 0; }

}

class S : T {

   char[] str;
   this(S s = null) {
       super(s);
       if (s !is null)
           str = s.str.dup; // do the deep-copy
       else // all newly created will get that
           str = "123".dup;
   }
   char[] toString() { return "I'm the instance of S p: " ~ str; }
   override
   T duplicate() { return new S(this); }
   // additional proc, just to test deep-copy
   override
   int custom(int x) {   
       if (str !is null) str[0] = x;
       return str is null;
   }

}

void main () {

   T orig = new S;
   orig.custom('X');
   T copy = orig.duplicate;
   orig.custom('Y');
   Stdout (orig).newline;
   Stdout (copy).newline; // should have 'X' at the beginning

}</lang>

E

In E, a generic copy for all objects can be built out of the serialization facility, by connecting an object recognizer to an object builder without even using any intermediate serialized form:

<lang e>def deSubgraphKit := <elib:serial.deSubgraphKit>

def copy(object) {

   return deSubgraphKit.recognize(object, deSubgraphKit.makeBuilder())

}</lang>

Since E does not have any static dispatch, this cannot be non-polymorphic without also being non-generic.

An example showing that it does indeed make copies follows. (For the task description, let S be the type of all serializable E objects, T be the FlexList type (result of diverge), and the overriden method be push.

<lang e>? def a := [1].diverge()

  1. value: [1].diverge()

? def b := copy(a)

  1. value: [1].diverge()

? b.push(2) ? a

  1. value: [1].diverge()

? b

  1. value: [1, 2].diverge()</lang>

JavaScript

Copied from here: <lang javascript>function clone(obj){

   if (obj == null || typeof(obj) != 'object')
       return obj;
   var temp = {};
   for (var key in obj)
       temp[key] = clone(obj[key]);
   return temp;

}</lang>

Java

Here we implement a "copy" method once in the base class because there is by default no public way to copy an object from outside the class in Java. (There is a protected, not public, "clone" method inherited from Object.) <lang java>class T implements Cloneable {

   public String name() { return "T"; }
   public T copy() {
       try {
           return (T)super.clone();
       } catch (CloneNotSupportedException e) {
           return null;
       }
   }

}

class S extends T {

   public String name() { return "S"; }

}

public class PolymorphicCopy {

   public static T copier(T x) { return x.copy(); }
   public static void main(String[] args) {
       T obj1 = new T();
       S obj2 = new S();
       System.out.println(copier(obj1).name()); // prints "T"
       System.out.println(copier(obj2).name()); // prints "S"
   }

}</lang>

Objective-C

All objects inherit the copy method from NSObject, which performs copying. But they must implement the copyWithZone: method to actually specify how to copy. Calling copy on an object that does not implement copyWithZone: will result in an error.

Implementing copying can be done with the NSCopyObject() function for a simple byte-for-byte shallow copy of the object fields. Or you can do something more complicated like explicitly allocate a new object of a certain class (but then it won't really be polymorphic) and initialize it with your object's data. I am not really clear on the details of this.

<lang objc>@interface T : NSObject { } - (void)identify; @end

@implementation T - (void)identify {

   NSLog(@"I am a genuine T");

} - (id)copyWithZone:(NSZone *)zone {

   return NSCopyObject(self, 0, zone);
   // or:
   // T *copy = [[T allocWithZone:zone] init]; // call an appropriate constructor here
   //                                          // then copy data into it as appropriate here
   // return copy;

} @end

@interface S : T { } @end

@implementation S - (void)identify {

   NSLog(@"I am an S");

} @end

int main() {

   T *original = [[S alloc] init];
   T *another = [original copy];
   [another identify]; // logs "I am an S"
   [another release]; // like "alloc", the object returned by "copy" is "owned" by the caller, so we are responsible for releasing it
   [original release];
   return 0;

}</lang>

Analogously, there is a mutableCopy method to get a mutable copy of the current object (e.g. if you have an NSArray object and you want an NSMutableArray with the same contents). In this case it would have to implement the mutableCopyWithZone: method to specify how to copy.

OCaml

I decided not to use classes and inheritance here because structural subtyping is more natural in OCaml. Oo.copy is polymorphic over all object types. <lang ocaml>let obj1 =

 object
   method name = "T"
 end

let obj2 =

 object
   method name = "S"
 end

let () =

 print_endline (Oo.copy obj1)#name; (* prints "T" *)
 print_endline (Oo.copy obj2)#name; (* prints "S" *)</lang>

PHP

<lang php><?php class T {

     function name() { return "T"; }

}

class S {

     function name() { return "S"; }

}

$obj1 = new T(); $obj2 = new S(); $obj3 = clone $obj1; $obj4 = clone $obj2; echo $obj3->name(), "\n"; // prints "T" echo $obj4->name(), "\n"; // prints "S" ?></lang>

Python

<lang python>import copy

class T:

  def classname(self): 
     return self.__class__.__name__
  def __init__(self):
     self.myValue = "I'm a T."
  def speak(self):
     print self.classname(), 'Hello', self.myValue
  def clone(self):
     return copy.copy(self)

class S1(T):

  def speak(self):
     print self.classname(),"Meow", self.myValue

class S2(T):

  def speak(self):
     print self.classname(),"Woof", self.myValue


print "creating initial objects of types S1, S2, and T" a = S1() a.myValue = 'Green' a.speak()

b = S2() b.myValue = 'Blue' b.speak()

u = T() u.myValue = 'Purple' u.speak()

print "Making copy of a as u, colors and types should match" u = a.clone() u.speak() a.speak() print "Assigning new color to u, A's color should be unchanged." u.myValue = "Orange" u.speak() a.speak()

print "Assigning u to reference same object as b, colors and types should match" u = b u.speak() b.speak() print "Assigning new color to u. Since u,b references same object b's color changes as well" u.myValue = "Yellow" u.speak()

b.speak()</lang>Output of the above program is as follows

creating initial objects of types S1, S2, and T
S1 Meow Green
S2 Woof Blue
T Hello Purple
Making copy of a as u, colors and types should match
S1 Meow Green
S1 Meow Green
Assigning new color to u, A's color should be unchanged.
S1 Meow Orange
S1 Meow Green
Assigning u to reference same object as b, colors and types should match
S2 Woof Blue
S2 Woof Blue
Assigning new color to u. Since u,b references same object b's color changes as well
S2 Woof Yellow
S2 Woof Yellow

The foregoing example uses the Python standard library copy module. The task, as stated,does not provide insight as to what should happen should the object contain lists of other objects. It could be necessary to use "deep copy" instead of copy. (using copy.deepcopy). The distinction is important for complex objects containing references to other objects (for instances lists, tuples or dictionaries containing other lists, tuples or dictionaries as elements). The described task, as presented, offers no guidance on this matter.

In many cases the most portable and robust "copy" would be made by serializing the source object and then de-serializing it back into the target. Under Python this would best be done with the pickle or cPickle standard modules.

<lang python>import cPickle as pickle

source = {'a': [1, 2.0, 3, 4+6j],

        'b': ('string', u'Unicode string'),
        'c': None}

target = pickle.loads(pickle.dumps(source))</lang>

In this example we use the cPickle module which is an implementation of the pickle features coded in C for optimal performance. We import it as pickle since we intend to use only those features which are common to both implementations. (The pure Python implementation is retained for those who which to create their own classes derived therefrom). The dumps() and loads() methods dump the data structures to a string and load them from a string, respectively. (The more common use of pickle is to serialize the data out to a file or over a network connection using file-like .read() and .write() methods. For those we'd use the pickle.dump() and pickle.load() methods).

For the simplest cases one can use simple Python introspection to copy simple objects:

<lang python>target = source.__class__() # Create an object of the same type if hasattr(source, 'items') and callable(source.items):

   for key,value in source.items:
       target[key] = value

elif hasattr(source, '__len__'):

   target = source[:]

else: # Following is not recommended. (see below).

   target = source</lang>

This example handles dictionaries (and anything that implements a sufficiently dictionary like interface to support the items() method along with the __setitem__() method. (statements of the form x[y] = z in Python are implicitly calling the __setitem__() method of the "x" object, passing it a key of "y" and a value of "z." Similarly this code tests if an item is a sequence (one can call the "len()" built-in function on it) and, if so, uses a slice assignment to perform a shallow copy. For any other type of object a simple binding is performed. Technically this last case will not "copy" anything ... it will create a new name binding to the object to which "source" was already a reference. The earlier binding of a "blank" instance of the source's __class__ will be replaced. So the trick of creating the blank object of the same type is only meaningful for the other types. In the cases of strings, integers and other numbers the objects themselves are immutable and the bindings are all dynamic (so the entire task is moot with respect to them).

Ruby

All Ruby objects inherit two methods for copying themselves: "clone" and "dup". I don't really understand the difference between them. <lang ruby>class T

 def name
   "T"
 end

end

class S

 def name
   "S"
 end

end

obj1 = T.new obj2 = S.new puts obj1.dup.name # prints "T" puts obj2.dup.name # prints "S"</lang>

Slate

All objects in Slate may be cloned unless they are clones of Oddball (like True, False, and Nil) or are word-size Integers (which are encoded as tagged pointer values). This is a shallow copy where no values held in the object's slots are replaced with copies. There is also a copy method which is universal and overridden to perform deep copies as appropriate - copying continues via recursion through slot values and should be modified on any type where equality (=) is overridden.

<lang slate>define: #T &parents: {Cloneable}.

define: #S &parents: {Cloneable}.

define: #obj1 -> T clone. define: #obj2 -> S clone.

obj1 printName. obj2 printName.</lang>

Tcl

Tcl values are logically immutable, and are passed around by reference with copies being taken as and when it is necessary to do so to maintain the immutable model. Hence an effective copy of any value is just: <lang tcl>set varCopy $varOriginal</lang> With objects, slightly more work is required because they are normally passed around by name/reference.

Works with: Tcl version 8.6

<lang tcl>oo::class create CanClone {

   method clone {{name {}}} {
       # Make a bare, optionally named, copy of the object
       set new [oo::copy [self] {*}[expr {$name eq "" ? {} : [list $name]}]]
       # Reproduce the basic variable state of the object
       set newns [info object namespace $new]
       foreach v [info object vars [self]] {
           namespace upvar [namespace current] $v v1
           namespace upvar $newns $v v2
           if {[array exists v1]} {
               array set v2 [array get v1]
           } else {
               set v2 $v1
           }
       }
       # Other object state is possible like open file handles. Cloning that is
       # properly left to subclasses, of course.
       return $new
   }

}

  1. Now a demonstration

oo::class create Example {

   superclass CanClone
   variable Count Name
   constructor {name} {set Name $name;set Count 0}
   method step {} {incr Count;return}
   method print {} {puts "this is $Name in [self], stepped $Count times"}
   method rename {newName} {set Name $newName}

} set obj1 [Example new "Abracadabra"] $obj1 step $obj1 step $obj1 print set obj2 [$obj1 clone] $obj2 step $obj2 print $obj2 rename "Hocus Pocus" $obj2 print $obj1 print</lang> Which produces this output (object names might vary if you run it):

this is Abracadabra in ::oo::Obj5, stepped 2 times
this is Abracadabra in ::oo::Obj6, stepped 3 times
this is Hocus Pocus in ::oo::Obj6, stepped 3 times
this is Abracadabra in ::oo::Obj5, stepped 2 times