Multiple distinct objects

From Rosetta Code
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 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.

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

This task mostly makes sense for languages operating in the pass-references-by-value style (most object-oriented or 'dynamic' languages).

Ada

<ada> A : array (1..N) of T; </ada> Here N can be unknown until run-time. T is any constrained type.

C

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

 init_foo(&foos[i]);

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

C++

Using only language primitives: // 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;

Using the standard library

  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());

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:

  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)

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

Common Lisp

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

Haskell

If the creator of the distinct thing is in some monad, then one can write replicateM n makeTheDistinctThing in an appropriate do block. If it is distinguished by, say, a numeric label, one could write map makeTheDistinctThing [1..n]

Java

Works with: Java version 1.5+

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

   foos[i] = new Foo();
  1. incorrect version:

Foo[] foos_WRONG = new Foo[n]; Arrays.fill(foos, new Foo()); // new Foo() only evaluated once

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

   foos.add(new Foo());
  1. incorrect:

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

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. 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()); } 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)); }

Python

The mistake is often written as: [Foo()] * n # here Foo() can be any expression that returns a new object which is incorrect since Foo() is only evaluated once. A common correct version is: [Foo() for i in xrange(n)] 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: [Foo.new] * n # here Foo.new can be any expression that returns a new object Array.new(n, Foo.new) which are incorrect since Foo.new is only evaluated once. A common correct version is: Array.new(n) { Foo.new } 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.