Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Creating true copies (of objects) in JS (possible?)

Reply
Thread Tools

Creating true copies (of objects) in JS (possible?)

 
 
svend
Guest
Posts: n/a
 
      11-02-2003
I'm messing with some code here... Lets say I have this array:

a1 = [1,"2",new Array(2.5,3,3.5),4];

And I apply slice(0) on it, to create a copy:

a2 = a1.slice(0);

But this isn't a true copy. If I go a1[2][1] = 42, and then
alert(a2[2][1]) I will see 42 there too. I'm doubting, but I was
wondering if there is a general solution to this problem?

Regards,
Svend
 
Reply With Quote
 
 
 
 
Janwillem Borleffs
Guest
Posts: n/a
 
      11-02-2003

"svend" <(E-Mail Removed)> schreef in bericht
news:(E-Mail Removed) om...
>
> But this isn't a true copy. If I go a1[2][1] = 42, and then
> alert(a2[2][1]) I will see 42 there too. I'm doubting, but I was
> wondering if there is a general solution to this problem?
>


The problem is caused by the array within the array. While slice returns a
copy of the non-array elements, array elements remain references.

You could try to use a method that checks the types of the elements and
applies slice() upon array elements to provide a copy:

Array.prototype.clone = function () {
var tmp = [];
for (i in this) {
if (this[i].constructor == Array) {
tmp[i] = this[i].slice(0);
} else {
tmp[i] = this[i];
}
}
return tmp;
}

var a1 = [1,"2",new Array(2.5,3,3.5),4];
var a2 = a1.clone();
a1[2][1] = 42;
alert(a2[2][1]);

Note that this example doesn't work with arrays within array elements.


JW



 
Reply With Quote
 
 
 
 
Lasse Reichstein Nielsen
Guest
Posts: n/a
 
      11-02-2003
"Janwillem Borleffs" <(E-Mail Removed)> writes:

> Array.prototype.clone = function () {
> var tmp = [];
> for (i in this) {
> if (this[i].constructor == Array) {
> tmp[i] = this[i].slice(0);


Why not
tmp[i] = this[i].clone();
?

> Note that this example doesn't work with arrays within array elements.


Then it would.

/L
--
Lasse Reichstein Nielsen - http://www.velocityreviews.com/forums/(E-Mail Removed)
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
 
Reply With Quote
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-02-2003
On Sun, 2 Nov 2003, Lasse Reichstein Nielsen wrote:

> > Array.prototype.clone = function () {
> > var tmp = [];
> > for (i in this) {
> > if (this[i].constructor == Array) {
> > tmp[i] = this[i].slice(0);

>
> Why not
> tmp[i] = this[i].clone();
> ?


The method of looping into nested array elements will only work for
primitives. I was looking for a more generic method (without hope
of there being one). As far as I can tell, there's no way of
cloning x = new MyOwnObject();

And imagine if an array had itself as an element!

It's in conjunction with a curry function I'm interested in this. If there
is no way to assure the accumulated arguments integrity, special care
might be needed, as some code might have unintentional side effects.

Regards,
Svend
 
Reply With Quote
 
Lasse Reichstein Nielsen
Guest
Posts: n/a
 
      11-02-2003
"Svend Ezaki Tofte (DIKU)" <(E-Mail Removed)> writes:

> The method of looping into nested array elements will only work for
> primitives.


I don't understand. It should work for any Array (except recursive
ones).

It might not be what you want, though.

> I was looking for a more generic method (without hope of there being
> one). As far as I can tell, there's no way of cloning x = new
> MyOwnObject();


Not generally, no. That would require you to be able to find the
prototype object (which can be prevented), and even if you made a
verbatim copy, it might not be functional, because the methods
could have been created by the constructor and must be unique to
the object.

In object oriented thinking, you don't want to *copy* an object.
Objects have identity, and a copy of the object would not be the same
object.

If you know that your array is "just" an array (a container for
values), and is never used as a proper object, then copying makes
sense (an equivalent container for the same values). In that case,
you probably don't want to recurse anyway.

> And imagine if an array had itself as an element!


That is a problem. It can be fixed, though:

Array.prototype.clone = function() {
var tmp = [];
this.clone = function(){return tmp;}
for (var i in this) {
if (i == "clone") {continue;}
if (this[i].constructor == Array) {
tmp[i] = this[i].clone();
} else {
tmp[i] = this[i];
}
}
delete this.clone;
return tmp;
}


> It's in conjunction with a curry function I'm interested in this. If there
> is no way to assure the accumulated arguments integrity, special care
> might be needed, as some code might have unintentional side effects.


If you use functional methods like currying at the same time as side
effects, you should sit down and decide which behavior is the one you
want. There are probably several different ways the two can interact that
can all make sense. Good luck

Umm. Curry.
---
function curry(func,num) {
var accumulator = [];
var self;
return function dummy() {
self = self || this;
for (var i=0;i<arguments.length;i++) {
accumulator[accumulator.length] = arguments[i];
}
if (accumulator.length>=num) {
return func.apply(self,accumulator);
} else {
return dummy;
}
}
}
---

/L
--
Lasse Reichstein Nielsen - (E-Mail Removed)
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
 
Reply With Quote
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-03-2003
On Mon, 3 Nov 2003, Lasse Reichstein Nielsen wrote:

> [lrn mentions alot of sensible stuff]


Well, those are all true, and I havedn't honestly thought this through
very well. I just wanted to see, if this was possible.

> That is a problem. It can be fixed, though:
>
> Array.prototype.clone = function() {
> var tmp = [];
> this.clone = function(){return tmp;}
> for (var i in this) {
> if (i == "clone") {continue;}
> if (this[i].constructor == Array) {
> tmp[i] = this[i].clone();
> } else {
> tmp[i] = this[i];
> }
> }
> delete this.clone;
> return tmp;
> }


Clever enough! I wouldn't have thought of that...

> If you use functional methods like currying at the same time as side
> effects, you should sit down and decide which behavior is the one you
> want. There are probably several different ways the two can interact that
> can all make sense. Good luck


Very true. There's a longer history behind this post though. I asked here,
a few months ago, on how to generally curry things. I got an answer, much
like the one you've given below.

I found it had some problems though, as it worked via a closure. Each time
a function had accumulated enough arguments to execute, it executed,
returning the correct result. But any subsequent calls to the functions,
would yield the first result, as the previous arguments where not
forgotten.

I fixed this, by making it reset to earlier accumulated arguments, after
it had executed, but before returning a result.

In returning the accumulator function, things became even trickier, since
this is essentially nested closures. I couldn't think of a sane way to do
it using closures, so I just changed the functions, so the accumulated
arguments were a function parameter.

So, I think I've succeeded in eliminating all side effects of the currying
function, except the whole reference instead of by value. Of course, you
can shrug it off. JavaScript simply isn't a purely functional language, so
trying to make into one, is doomed to fail. Still, I like the ability to
curry stuff. It's kinda handy when you're lazy (I know it doesn't save
cycles). Plus, it's pretty geeky

> function curry(func,num) {
> var accumulator = [];
> var self;
> return function dummy() {
> self = self || this;
> for (var i=0;i<arguments.length;i++) {
> accumulator[accumulator.length] = arguments[i];
> }
> if (accumulator.length>=num) {
> return func.apply(self,accumulator);
> } else {
> return dummy;
> }
> }
> }


I'm on my way to bed, so I'm not sure how your function works, but as far
as I can see, it wouldn't handle the closures well would it?

I made a write up here, btw, using material from the last time I posted
here

http://www.svendtofte.com/code/curried_javascript/

Regards,
Svend
 
Reply With Quote
 
Lasse Reichstein Nielsen
Guest
Posts: n/a
 
      11-03-2003
"Svend Ezaki Tofte (DIKU)" <(E-Mail Removed)> writes:

> I found it had some problems though, as it worked via a closure. Each time
> a function had accumulated enough arguments to execute, it executed,
> returning the correct result. But any subsequent calls to the functions,
> would yield the first result, as the previous arguments where not
> forgotten.


Doh. Yes, ofcourse. I hadn't thought of that. Each call to the original
function should yield a *new* function, not the same old function with
an accumulator that have changed.

---
function curry(func,args) { // args is optional argument. Must be array.
if (!args) {args = [];}
return function() {
var newargs = args.slice[0];
for (var i=0;i<arguments.length;i++) {
newargs.push(arguments[i]);
}
if (newargs.length >= func.length) {
return func.apply(this,newargs);
} else {
return curry(func,newargs);
}
}
}
---
(I hadn't thought of func.length! Smart!)

Then you can write:
---
function add(x,y,z) {return x+y+z;}
var cadd = curry(add);
var cadd1 = cadd(1);
var cadd2 = cadd(2);
var cadd13 = cadd1(3);
var cadd15 = cadd1(5);
var cadd27 = cadd2(7);
alert(cadd13(10)+cadd15(9)+cadd27(13));
---

> I fixed this, by making it reset to earlier accumulated arguments, after
> it had executed, but before returning a result.


That still only allows you to use the curried function sequentially. You
can't give it one argument, and then call the resulting function twice
on different second arguments.

> In returning the accumulator function, things became even trickier, since
> this is essentially nested closures. I couldn't think of a sane way to do
> it using closures, so I just changed the functions, so the accumulated
> arguments were a function parameter.


Hey, me too!

> So, I think I've succeeded in eliminating all side effects of the currying
> function, except the whole reference instead of by value. Of course, you
> can shrug it off. JavaScript simply isn't a purely functional language, so
> trying to make into one, is doomed to fail. Still, I like the ability to
> curry stuff. It's kinda handy when you're lazy (I know it doesn't save
> cycles). Plus, it's pretty geeky


What I can't see is why it is a problem that arrays and objects are passed
by reference. They are that in non-curried Javascript too. Example:
---
var a = [1,2,3];
function foo() {
a[0]=37;
a[1]=42;
return 13;
}
function bar(arr,func) {
return arr[0]+func()+arr[1];
}
bar(a,foo);
---
Side-effects can happen inside the function itself. I don't see a problem
in them happening between calls to a curried function.

[bad curry]
> I'm on my way to bed, so I'm not sure how your function works, but as far
> as I can see, it wouldn't handle the closures well would it?


No.

> I made a write up here, btw, using material from the last time I posted
> here
>
> http://www.svendtofte.com/code/curried_javascript/


I am getting tired too
I can't seem to get the examples working ...
Ah. I removed the "this." in front of "add", and then it worked. Must
be because I as running it inside a call to eval or something. I don't
think you need the "this."-prefix. You can refer directly to a named
function from inside itself.

It seems to be working fine now.


Btw, in your "Object locator for Netscape 4", you really shouldn't
use a browser detector. Object detection is safer.

Svend Tofte ... Are you by any chance related to Mads?
/L
--
Lasse Reichstein Nielsen - (E-Mail Removed)
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
 
Reply With Quote
 
Dom Leonard
Guest
Posts: n/a
 
      11-03-2003
Lasse Reichstein Nielsen wrote:
> "Svend Ezaki Tofte (DIKU)" writes:
>
>
>>I found it had some problems though, as it worked via a closure. Each time
>>a function had accumulated enough arguments to execute, it executed,
>>returning the correct result. But any subsequent calls to the functions,
>>would yield the first result, as the previous arguments where not
>>forgotten.


From code at the URL quotes below can see how you (Svend) solved the
"problem" incurred by not having separate copies of accumulated
arguments, but would point out that the inner function

return function (){
// arguments are params, so closure bussiness is avoided.
return accumulator(arguments,sa.slice(0),n);
}

does not prevent nested closures from being created. It does defeat
reference to outer closure variables and parameter names by using "sa"
and "n" as formal parameter names of the "accumulator" function called.

FWIW, the alternative of copying both previous and freshly supplied
arguments to a new accumulation array obviates the need to consider
reinstatement of closure variables, and using the (make) curry function
itself to accumulate old and new argument arrays can avoid the creation
of deeply nested closures.


>
> Each call to the original
> function should yield a *new* function, not the same old function with
> an accumulator that have changed.
>
> ---
> function curry(func,args) { // args is optional argument. Must be array.
> if (!args) {args = [];}
> return function() {
> var newargs = args.slice[0];


This code is not unlike some I posted some months back when discussing
curried functions, so I am familiar with its behaviour. I concur that
args must be an Array object since argument objects within functions
lack Array.prototype methods and hence the .slice method called here.

The down-side to requiring "args" to be an array, however, is that a
function currying itself must explicitly convert its argument object
into an array when calling "curry".

My comment is that copying old and new arguments into a third array
explicitly created as an Array object within "curry" can simplify the
calling process.


<snip>


>>
>>http://www.svendtofte.com/code/curried_javascript/

>
>


> ... to get the examples working ...
> Ah. I removed the "this." in front of "add", and then it worked. Must
> be because I as running it inside a call to eval or something. I don't
> think you need the "this."-prefix. You can refer directly to a named
> function from inside itself.
>



I would tend to put that more strongly myself: remove the "this." in
front of "add". Not just to run within an evaluator, but because it
actually requires that "add" be a method of its "this" value. This is
false in javascript if "add" were defined as a nested function and
called without object qualification. For those unfamiliar with the
outcome, "this" within "add" would take on the value of the global
object in such a case.



HTH

Dom


 
Reply With Quote
 
Dr John Stockton
Guest
Posts: n/a
 
      11-03-2003
JRS: In article <(E-Mail Removed)> , seen
in news:comp.lang.javascript, Svend Ezaki Tofte (DIKU)
<(E-Mail Removed)> posted at Sun, 2 Nov 2003 23:01:47 :-
>
>And imagine if an array had itself as an element!


If that can happen, then an instance of a recursive process needs to
check whether it has arrived at an ancestor, or employ other means to
prevent indefinite recursion. Or the process documentation should
forbid the case.

--
John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4
<URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang.javascript
<URL:http://www.merlyn.demon.co.uk/js-index.htm> JS maths, dates, sources.
<URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/JS/&c., FAQ topics, links.
 
Reply With Quote
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-03-2003
On Mon, 3 Nov 2003, Lasse Reichstein Nielsen wrote:

> Doh. Yes, ofcourse. I hadn't thought of that. Each call to the original
> function should yield a *new* function, not the same old function with
> an accumulator that have changed.


I wrote that page a few months ago, and wanting to use it the other day,
found absolutely nothing worked. Surprise! I messed with it a little, and
figured out it was the closures giving the problems.

> function curry(func,args) { // args is optional argument. Must be array.
> if (!args) {args = [];}
> return function() {
> var newargs = args.slice[0];
> for (var i=0;i<arguments.length;i++) {
> newargs.push(arguments[i]);
> }
> if (newargs.length >= func.length) {
> return func.apply(this,newargs);
> } else {
> return curry(func,newargs);
> }
> }
> }
> ---
> (I hadn't thought of func.length! Smart!)
>
> Then you can write:
> ---
> function add(x,y,z) {return x+y+z;}
> var cadd = curry(add);
> var cadd1 = cadd(1);
> var cadd2 = cadd(2);
> var cadd13 = cadd1(3);
> var cadd15 = cadd1(5);
> var cadd27 = cadd2(7);
> alert(cadd13(10)+cadd15(9)+cadd27(13));
> ---


I'll have to test this code out. From the looks of it, I don't see how it
would work. Wouldn't fx. cadd13 always return 14 now? Because it's
newsargs array would contain 1, 3, and 10?

I'm in a bit stripped for time right now, so I unfortunantly don't have
the time to play around with the code as I want to. But the thread will be
bookmarked in google groups, and I'll have to return, if any questions
arise

> > I fixed this, by making it reset to earlier accumulated arguments, after
> > it had executed, but before returning a result.

>
> That still only allows you to use the curried function sequentially. You
> can't give it one argument, and then call the resulting function twice
> on different second arguments.


What do you mean?

> > In returning the accumulator function, things became even trickier, since
> > this is essentially nested closures. I couldn't think of a sane way to do
> > it using closures, so I just changed the functions, so the accumulated
> > arguments were a function parameter.

>
> Hey, me too!


Does this refer to the above also?

> Side-effects can happen inside the function itself. I don't see a problem
> in them happening between calls to a curried function.


I suppose you are right. It's not like it's preventable anyway. I'm sure
some very unreadable code can be made that way

> > I made a write up here, btw, using material from the last time I posted
> > here
> >
> > http://www.svendtofte.com/code/curried_javascript/

>
> I am getting tired too
> I can't seem to get the examples working ...
> Ah. I removed the "this." in front of "add", and then it worked. Must
> be because I as running it inside a call to eval or something. I don't
> think you need the "this."-prefix. You can refer directly to a named
> function from inside itself.


I will remove the this. prefix. I'm not really sure why I had it there...
But how would this behave then, for anonymous functions?

> It seems to be working fine now.
>
>
> Btw, in your "Object locator for Netscape 4", you really shouldn't
> use a browser detector. Object detection is safer.


Heh, don't hold that against me. That was written three or four years ago,
as a first year CS student told me about recursion, to navigate the NS4 dom
tree... ah, those were the days!

> Svend Tofte ... Are you by any chance related to Mads?


Heh, second time I get that, and no

Regards,
Svend
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[False,True] and [True,True] --> [True, True]????? bdb112 Python 45 04-29-2009 02:35 AM
"0 in [True,False]" returns True Pierre Quentel Python 59 12-16-2005 01:47 PM
TurboGears /.-ed, >new == True< or >new == "True"< Andy Leszczynski Python 4 10-13-2005 06:56 AM
C and C++ are interoperable languages? True or Not True? Chip C++ 6 01-08-2005 11:10 PM
Does true ^ true return false? Siemel Naran C++ 19 06-18-2004 11:06 AM



Advertisments