Multiple distinct objects: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Tcl}}: make it clearer that the class can be set at runtime (trivial for Tcl...))
Line 277: Line 277:
{{works with|Tcl|8.6}}
{{works with|Tcl|8.6}}
(or Tcl 8.5 with the TclOO package already required)
(or Tcl 8.5 with the TclOO package already required)
<lang Tcl># Wrong version; only a single object created
<lang Tcl># The class that we want to make unique instances of
set theList [lrepeat $n [Foo new]]
set theClass Foo

# Wrong version; only a single object created
set theList [lrepeat $n [$theClass new]]


# Right version; objects distinct
# Right version; objects distinct
set theList {}
set theList {}
for {set i 0} {$i<$n} {incr i} {
for {set i 0} {$i<$n} {incr i} {
lappend theList [Foo new]
lappend theList [$theClass new]
}</lang>
}</lang>

Revision as of 13:37, 7 June 2009

Task
Multiple distinct objects
You are encouraged to solve this task according to the task description, using any language you may know.

Create a sequence (array, list, whatever) consisting of n distinct, initialized items of the same type. n should be determined at runtime.

By distinct we mean that if they are mutable, changes to one do not affect all others; if there is an appropriate equality operator they are considered unequal; etc. The code need not specify a particular kind of distinction, but do not use e.g. a numeric-range generator which does not generalize.

By initialized we mean that each item must be in a well-defined state appropriate for its type, rather than e.g. arbitrary previous memory contents in an array allocation. Do not show only an initialization technique which initializes only to "zero" values (e.g. calloc() or int a[n] = {}; in C), unless user-defined types can provide definitions of "zero" for that type.

This task was inspired by the common error of intending to do this, but instead creating a sequence of n references to the same mutable object; it might be informative to show the way to do that as well.

This task is most relevant to languages operating in the pass-references-by-value style (most object-oriented, garbage-collected, and/or 'dynamic' languages).

Ada

<lang ada> A : array (1..N) of T; </lang> Here N can be unknown until run-time. T is any constrained type. In Ada all objects are always initialized, though some types may have null initialization. When T requires a non-null initialization, it is done for each array element. For example, when T is a task type, N tasks start upon initialization of A. Note that T can be a limited type like task. Limited types do not have predefined copy operation. Arrays of non-limited types can also be initialized by aggregates of: <lang ada> A : array (1..N) of T := (others => V); </lang> Here V is some value or expression of the type T. As an expression V may have side effects, in that case it is evaluated exactly N times, though the order of evaluation is not defined. Also an aggregate itself can be considered as a solution of the task: <lang ada> (1..N => V) </lang>

ALGOL 68

Translation of: python
Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386

<lang algol>MODE FOO = STRUCT(CHAR u,l); INT n := 26; [n]FOO f;

  1. Additionally each item can be initialised #

FOR i TO UPB f DO f[i] := (REPR(ABS("A")-1+i), REPR(ABS("a")-1+i)) OD;

print((f, new line))</lang> Output:

AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz

C

<lang c> foo *foos = malloc(n * sizeof(*foos)); for (int i = 0; i < n; i++)

 init_foo(&foos[i]);

</lang>

(Or if no particular initialization is needed, skip that part, or use calloc.)

C++

This example may be incorrect due to a recent change in the task requirements or a lack of testing. Please verify it and remove this message. If the example does not match the requirements or does not work, replace this message with Template:incorrect or fix the code yourself.

Using only language primitives: <lang cpp> // this assumes T is a default-constructible type (all built-in types are) T* p = new T[n]; // if T is POD, the objects are uninitialized, otherwise they are default-initialized // ... // when you don't need the objects any more, get rid of them delete[] p; </lang>

Using the standard library <lang cpp>

  1. include <vector>

// this assumes T is default-constructible std::vector<T> vec1(n); // all n objects are default-initialized

// this assumes t is a value of type T (or a type which implicitly converts to T) std::vector<T> vec2(n, t); // all n objects are copy-initialized with t

// if you want to evaluate an expression for each new object // (e.g. if it uses randomness to initialize the state or something) std::vector<T> vec3; for (int i = 0; i < n; ++i)

 vec3.push_back(makeDistinctObject());

</lang> Instead of vector, deque or list can be used. Note that with standard containers containing the object type itself (instead of a pointer), you don't need to explicitly destroy the objects.

For polymorphic objects: <lang cpp>

  1. include <vector>

// this assumes Base is a polymorphic type which has a clone method for polymorphic copying, // and p is a pointer to Base or to a type derived from Base.

// the following is NOT correct: std::vector<Base*> bvec_WRONG(n, p); // create n copies of p, which all point to the same opject p points to.

// nor is this: std::vector<Base*> bvec_ALSO_WRONG(n, p->clone()); // create n pointers to a single clone of *p

// the correct solution std::vector<Base*> bvec(n); for (int i = 0; i < n; ++i)

 bvec[i] = p->clone();     // "p->clone()" here can be replaced with any expression returning a pointer to a new object

// another correct solution // this solution avoids uninitialized pointers at any point std::vector<Base*> bvec2; for (int i = 0; i < n; ++i)

 bvec2.push_back(p->clone()); // "p->clone()" here can be replaced with any expression returning a pointer to a new object

// ...

// because the container contains pointers, the objects have to be explicitly deleted // because ordinary pointers don't have destructors, so can't delete themselves // using a smart pointer like boost::shared_ptr instead would make this step unnecessary for (int i = 0; i < bvec.size(); ++i)

 delete bvec[i];

// alternate iteration: for (std::vector<Base*>::iterator it = bvec2.begin(); it != bvec2.end(); ++i)

 delete *i;

bvec.clear(); // make sure the dangling pointers are not used any more bvec2.clear(); // (not necessary if bvec isn't used afterwards; alternatively,

              // set the pointers to NULL after deleting; again, using a smart pointer
              // would remove this need)

</lang> Of course, also in this case one can use the other sequence containers or plain new/delete instead of vector.

C#

<lang csharp>using System; using System.Linq; using System.Collections.Generic;

List<Foo> foos = Enumerable.Range(1, n).Select(x => new Foo()).ToList();</lang>

Common Lisp

The mistake is often written as one of these: <lang lisp> (make-list n :initial-element (make-the-distinct-thing)) (make-array n :initial-element (make-the-distinct-thing)) </lang> which are incorrect since (make-the-distinct-thing) is only evaluated once. A common correct version is: <lang lisp> (loop repeat n collect (make-the-distinct-thing)) </lang> which evaluates (make-the-distinct-thing) n times and collects each result in a list.

Haskell

Below, we are assuming that makeTheDistinctThing is a monadic expression (i.e. it has type m a where m is some monad, like IO or ST), and we are talking about distinctness in the context of the monad. Otherwise, this task is pretty meaningless in Haskell, because Haskell is referentially transparent (so two values that are equal to the same expression are necessarily not distinct) and all values are immutable. <lang haskell> replicateM n makeTheDistinctThing </lang> in an appropriate do block. If it is distinguished by, say, a numeric label, one could write <lang haskell> mapM makeTheDistinctThing [1..n] </lang>

An incorrect version: <lang haskell> do x <- makeTheDistinctThing

  return (replicate n x)

</lang>

Java

Works with: Java version 1.5+

simple array: <lang java> Foo[] foos = new Foo[n]; // all elements initialized to null for (int i = 0; i < foos.length; i++)

   foos[i] = new Foo();

// incorrect version: Foo[] foos_WRONG = new Foo[n]; Arrays.fill(foos, new Foo()); // new Foo() only evaluated once </lang>

simple list: <lang java5> List<Foo> foos = new ArrayList<Foo>(); for (int i = 0; i < n; i++)

   foos.add(new Foo());

// incorrect: List<Foo> foos_WRONG = Collections.nCopies(n, new Foo()); // new Foo() only evaluated once </lang>

Generic version for class given at runtime:

It's not pretty but it gets the job done. The first method here is the one that does the work. The second method is a convenience method so that you can pass in a String of the class name. When using the second method, be sure to use the full class name (ex: "java.lang.String" for "String"). InstantiationExceptions will be thrown when instantiating classes that you would not normally be able to call new on (abstract classes, interfaces, etc.). <lang java5> public static <E> List <E> getNNewObjects(int n, Class <? extends E> c){ List <E> ans = new LinkedList<E>(); try { for(int i=0;i<n;i++) ans.add(c.newInstance());//can't call new on generic classes } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return ans; }

public static List<Object> getNNewObjects(int n, String className) throws ClassNotFoundException{ return getNNewObjects(n, Class.forName(className)); } </lang>

Modula-3

This example is incorrect. Please fix the code and remove this message.

Details: It does not initialize the sequence.

Similar to the Ada version above: <lang modula3>VAR a: ARRAY OF T</lang> This creates an open array (an array who's size is not known until runtime) of distinct elements of type T.

Modula-3 does not define what values the elements of A have, but it does guarantee that they will be of type T.

OCaml

For arrays:

Incorrect: <lang ocaml> Array.make n (new foo);; (* here (new foo) can be any expression that returns a new object, record, array, or string *) </lang> which is incorrect since new foo is only evaluated once. A correct version is: <lang ocaml> Array.init n (fun _ -> new foo);; </lang>

Perl

incorrect: <lang perl> (Foo->new) x $n # here Foo->new can be any expression that returns a reference representing a new object </lang> which is incorrect since Foo->new is only evaluated once.

A correct version is: <lang perl> map { Foo->new } 1 .. $n; </lang> which evaluates Foo->new $n times and collects each result in a list.

Python

The mistake is often written as: <lang python> [Foo()] * n # here Foo() can be any expression that returns a new object </lang> which is incorrect since Foo() is only evaluated once. A common correct version is: <lang python> [Foo() for i in xrange(n)] </lang> which evaluates Foo() n times and collects each result in a list. This last form is also discussed here, on the correct construction of a two dimensional array.

Ruby

The mistake is often written as one of these: <lang ruby> [Foo.new] * n # here Foo.new can be any expression that returns a new object Array.new(n, Foo.new) </lang> which are incorrect since Foo.new is only evaluated once. A common correct version is: <lang ruby> Array.new(n) { Foo.new } </lang> which evaluates Foo.new n times and collects each result in an Array. This last form is also discussed here, on the correct construction of a two dimensional array.

Smalltalk

<lang smalltalk>|c| "Create an ordered collection that will grow while we add elements" c := OrderedCollection new. "fill the collection with 9 arrays of 10 elements; elements (objects)

are initialized to the nil object, which is a well-defined 'state'"

1 to: 9 do: [ :i | c add: (Array new: 10) ]. "However, let us show a way of filling the arrays with object number 0" c := OrderedCollection new. 1 to: 9 do: [ :i | c add: ((Array new: 10) copyReplacing: nil withObject: 0) ]. "demonstrate that the arrays are distinct: modify the fourth of each" 1 to: 9 do: [ :i | (c at: i) at: 4 put: i ]. "show it" c do: [ :e | e printNl ].</lang>

Tcl

Works with: Tcl version 8.6

(or Tcl 8.5 with the TclOO package already required) <lang Tcl># The class that we want to make unique instances of set theClass Foo

  1. Wrong version; only a single object created

set theList [lrepeat $n [$theClass new]]

  1. Right version; objects distinct

set theList {} for {set i 0} {$i<$n} {incr i} {

   lappend theList [$theClass new]

}</lang>