473,408 Members | 2,888 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,408 software developers and data experts.

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

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
Jul 20 '05 #1
20 3070

"svend" <sv********@svendtofte.com> schreef in bericht
news:ef**************************@posting.google.c 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

Jul 20 '05 #2
"Janwillem Borleffs" <jw@jwscripts.com> 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 - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 20 '05 #3
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
Jul 20 '05 #4
"Svend Ezaki Tofte (DIKU)" <di******@diku.dk> 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 - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 20 '05 #5
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
Jul 20 '05 #6
"Svend Ezaki Tofte (DIKU)" <di******@diku.dk> 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 - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 20 '05 #7
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
Jul 20 '05 #8
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
Jul 20 '05 #9
On Mon, 3 Nov 2003, Dom Leonard wrote:
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.
I know closures are not prevented. But accumulation of arguments are
prevented. Which was the goal. Closures prevented this, argument passing
solved this.
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.
I don't understand this?
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.


Reminds me of a paper Waldemar wrote on JS 2.0. That if you understood
that, you've been reading too many language specs ;) [1]

Everytime I feel like I know JS, I see more of the language, and just how
truly twisted it really is... !

Regards,
Svend

[1] http://www.mozilla.org/js/language/evolvingJS.pdf (page 5)
Jul 20 '05 #10
JRS: In article <Pi*******************************@brok.diku.dk> , seen
in news:comp.lang.javascript, Svend Ezaki Tofte (DIKU)
<di******@diku.dk> 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.
Jul 20 '05 #11
Svend Ezaki Tofte (DIKU) wrote:
On Mon, 3 Nov 2003, Dom Leonard wrote:
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.

I don't understand this?


Now we both have a problem, I'm confused too :)

Looking at your code more closely I am left wondering if the "need to
reset" more properly belongs to a previous version of the function.
Rewritten to take out the outer variables which are hidden by parameter
names in nested function "accummulator", I got:
function curry(func,args,space)
{
function accumulator(moreArgs, cumeArgs, n)
{

// copy existing and number of remaining

var saPrev = cumeArgs.slice(0); // shallow copy
var nPrev = n; // to reset

// extend parameter array

for(var i=0;i<moreArgs.length;i++,n--) {
cumeArgs[cumeArgs.length] = moreArgs[i];
}

// if enough to execute, execute function
// and restore arguments (which are held in closure)

if ((n-moreArgs.length)<=0) {
var res = func.apply(space,cumeArgs);
// reset vars, so curried function can be applied to new params.
// cumeArgs = saPrev;
// n = nPrev;
return res;
}
else
{
return function (){
// arguments are params, so closure bussiness is avoided.
return accumulator(
arguments,
cumeArgs.slice(0), // shallow copy
n // updated arguments to come
);
}
}
}

return accumulator(
[],
Array.prototype.slice.apply(args), // shallow copy
func.length - args.length //arguments still to come
);
}

----

Curried functions appears to copy existing, accumulated arguments into a
formal parameter array passed to "accumulator" executing and creating a
closure one level down from the call. Effectively this parameter array
is the "new accumulation" array I saw as logically required.

What I now can't see, or find examples for, is why there would be a need
to restore the parameter array when a nested call to "accumulator"
executes "func" - should not the copy of accumulated arguments in its
2nd formal parameter go out of existence anyway?
Regards,

Dom

Jul 20 '05 #12
On Wed, 5 Nov 2003, Dom Leonard wrote:
Looking at your code more closely I am left wondering if the "need to
reset" more properly belongs to a previous version of the function.
Rewritten to take out the outer variables which are hidden by parameter
names in nested function "accummulator", I got:

[lotsa code snipped]
Mmmm, I like that even more. Of course, totally parametizing the arguments
eliminates the whole closure bussiness even more. I'll have to rip that
for my script ;)
What I now can't see, or find examples for, is why there would be a need
to restore the parameter array when a nested call to "accumulator"
executes "func" - should not the copy of accumulated arguments in its
2nd formal parameter go out of existence anyway?


But, and this is without testing your code (I see you've commented out the
reset part I had in there), the function will remember the passed
arguments.
Or what? I'm not sure why it should fall out of scope, and just be
forgotten?
The function returned by curry, accumulator, will keep accumulating
arguments,
and will never remove any.

Call it again and again, and it'll stick the new variables onto the end of
all the old ones.

Or am I misunderstanding your point? :)

Regards,
Svend
Jul 20 '05 #13
"Dom Leonard" <do*************@senet.andthis.com.au> wrote in message
news:S_*****************@nnrp1.ozemail.com.au...
<snip>
Looking at your code more closely I am left wondering if the
"need to reset" more properly belongs to a previous version
of the function. Rewritten to take out the outer variables
which are hidden by parameter names in nested function
"accummulator", I got:

function curry(func,args,space) <snip> }

----
Curried functions appears to copy existing, accumulated arguments
into a formal parameter array passed to "accumulator" executing
and creating a closure one level down from the call. Effectively
this parameter array is the "new accumulation" array I saw as
logically required. What I now can't see, or find examples for, is why there would be
a need to restore the parameter array when a nested call to
"accumulator" executes "func" - should not the copy of accumulated
arguments in its 2nd formal parameter go out of existence anyway?


This is an intriguing problem and I thought that I would have a go at
it. I didn't like the idea of accumulating the parameters in an array so
I traded that off against an increased number of function objects, but
along the way I gained the ability to pass some, or all, of the
arguments to the original call to the curry function with the function
that is to be executed.

This is my version:-

<html>
<head>
<title></title>
<script type="text/javascript">

function curry(f){
var obj = this; //global, unless this function
//is assigned as a method.
arguments.callee.depth = 0;
function getArgFunc(args, af){
function argFunc(x){
var retVal;
var self = arguments.callee;
function getArgs(){
var ar = self.getArgs();
ar[ar.length] = x;
return ar;
}
if(self.depth == f.length){
retVal = f.apply(obj, getArgs());
}else{
retVal = getArgFunc(arguments, getArgs);
}
return retVal;
}
argFunc.depth = args.callee.depth+1;
argFunc.getArgs = af;
for(var c = 1;c < args.length;c++){
argFunc = argFunc(args[c]);
if((typeof argFunc != 'function')||(!argFunc.getArgs)){
break;
}
}
args = null;
return argFunc;
}
return getArgFunc(arguments, function(){return [];});
}

</script>
</head>
<body>

<script type="text/javascript">

function add(x,y,z,a) {return x+','+y+','+z+','+a;}

var cadd = curry(add);

var cadd1 = cadd(1);
var cadd2 = cadd(2);
var cadd13 = cadd1(3);
var cadd15 = cadd1(5);
var cadd27 = cadd2(7);

var cadd13A = cadd13(10);
var cadd159 = cadd15(9);
var cadd27D = cadd27(13)
document.write('cadd13A(6) = '+cadd13A(6)+' :<br>');
document.write('cadd159(8) = '+cadd159(8)+' :<br>');
document.write('cadd27D(5) = '+cadd27D(5)+' :<br>');

document.write('cadd13A(1) = '+cadd13A(1)+' :<br>');
var cadd134 = cadd13(4);
document.write('cadd134(5) = '+cadd134(5)+' :<br>');

var cadd12 = cadd(1,2);
var cadd123 = cadd12(3);
document.write('cadd123(5) = '+cadd123(5)+' :<br>');
document.write('cadd12(6,7) = '+cadd12(6,7)+' :<br>');
document.write('curry(add,4,5,6,7) = '+curry(add,4,5,6,7)+' :<br>');
document.write('cadd2(4,6,8) = '+cadd2(4,6,8)+' :<br>');
document.write('cadd27(100,120) = '+cadd27(100,120)+' :<br>');
var cadd98 = curry(add,9,8)
document.write('cadd98(7,6) = '+cadd98(7,6)+' :<br>');
</script>
</body>
</html>

Richard.
Jul 20 '05 #14
Svend Ezaki Tofte (DIKU) wrote:
On Wed, 5 Nov 2003, Dom Leonard wrote:
<snip>

Of course, totally parametizing the arguments
eliminates the whole closure bussiness even more. I'll have to rip that
for my script ;)

Great, progress is in progress :)

What I now can't see, or find examples for, is why there would be a need
to restore the parameter array when a nested call to "accumulator"
executes "func" - should not the copy of accumulated arguments in its
2nd formal parameter go out of existence anyway?

But, and this is without testing your code (I see you've commented out the
reset part I had in there), the function will remember the passed
arguments.


Okay, I've got it now (well...)

A bug in the unmodified version was that calling "curry" shallow copied
arguments into local variable "sa", and then passed that array to
"accumulator" by *reference*.

The first call to "accumulator" now operated on an array held in the
outermost closure, and interferred with re-use of the outermost curried
function.

Copying existing arguments into a parameter array in the outermost
closure peformed a neccessary dynamic copy, made it consistent with the
same operation present in the closure created by "accumulator", and
hence removes the need to "reset" argument arrays (which probably was
not a perfect solution, but at this point who cares :)
Or what? I'm not sure why it should fall out of scope, and just be
forgotten?


If "accumulator" executes "func" it does not return a local nested
function, and so does not create a closure. On exit its formal
paraameters will not be held in scope anywhere. If the calling function
does not hold a variable set to the parameter array copied in call, it's
gone.

Hopefully you will now find creaation of nPrev and saPrev variables, and
the business of resetting parameter arrays unnecessary.

Regards,

Dom

Jul 20 '05 #15
Snap, we both posted at same time :)

It is certainly intriguing code usage, and I'll have a look at your
version when I get off line. For your interest and comparison, the
version I posted some monts ago went:

----

function curry(func,oldArgs)
{ function accumulator()
{ var newArgs = arguments;
if(newArgs.length ==0)
return accumulator; // no change
if(oldArgs)
{ newArgs=new Array();
newArgs.push.apply(newArgs,oldArgs);
newArgs.push.apply(newArgs,arguments);
}
return (newArgs.length >= func.length) ?
func.apply(this,newArgs) :
curry(func,newArgs);
}
return func.length ? accumulator : func;
}
----

It contains some "belts and braces" for boundary conditions that may not
be needed, and possibly could use Array.prototype.splice - I just didn't
see where at the time, and Svend's version is coming along too :)

Cheers,

Dom



Jul 20 '05 #16
"Dom Leonard" <do*************@senet.andthis.com.au> wrote in message
news:l6****************@nnrp1.ozemail.com.au...
Snap, we both posted at same time :) It is certainly intriguing code usage, and I'll have a look
at your version when I get off line. For your interest and
comparison, the version I posted some monts ago went:

----

function curry(func,oldArgs)
{ function accumulator()
{ var newArgs = arguments;
if(newArgs.length ==0)
return accumulator; // no change
That possibility didn't occur to me, but it does need to be covered.
if(oldArgs)
{ newArgs=new Array();
newArgs.push.apply(newArgs,oldArgs);
newArgs.push.apply(newArgs,arguments);
}
return (newArgs.length >= func.length) ?
func.apply(this,newArgs) :
curry(func,newArgs);
}
return func.length ? accumulator : func;
And thinking about the possibility that the parameter adding function
may be called without an argument it also occurred to me that the
function (func) itself may not require any parameters, which you have
covered here. However, if the function needs no parameters (func.length
== 0) shouldn't the value returned at this point be the result of
calling that function? As my version allows the initial call to curry to
be supplied with all of the required parameters and will execute the
function if they are all present I went with executing the function in
curry if func.length == 0.
} ----

It contains some "belts and braces" for boundary conditions
that may not be needed, and possibly could use
Array.prototype.splice - I just didn't see where at the time,
and Svend's version is coming along too :)


Being reminded of the belts and braces was useful, here are my
corrections for those (at least the ones I have noticed so far):-

function curry(f){
var retVal,obj = this; //global, unless this function
//is assigned as a method.
arguments.callee.depth = 0;
function getArgFunc(args, af){
function argFunc(x){
var retVal;
var argsLen = arguments.length;
var self = arguments.callee;
function getArgs(){
var ar = self.getArgs();
if(argsLen){ar[ar.length] = x;}
return ar;
}
if(self.depth >= f.length){
retVal = f.apply(obj, getArgs());
}else if(!argsLen){
retVal = self;
}else{
retVal = getArgFunc(arguments, getArgs);
}
return retVal;
}
argFunc.depth = args.callee.depth+1;
argFunc.getArgs = af;
for(var c = 1;c < args.length;c++){
argFunc = argFunc(args[c]);
if((typeof argFunc != 'function')||(!argFunc.getArgs)){
break;
}
}
af = (args = null);
return argFunc;
}
retVal = getArgFunc(arguments, function(){return [];});
return (f.length)?retVal:retVal();
}

Richard.
Jul 20 '05 #17
"Svend Ezaki Tofte (DIKU)" <di******@diku.dk> writes:
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?
No, cadd13's internal array is [1,3]. Each function gets a new array, so
they don't mess up each other's.

It can be shortened using some of the tricks you have used in the
later discussion, and made to not require arrays, but also accept
arguments objects:

function curry(func,args) { // args is optional argument.
if (!args) {args = [];}
return function curried() {
if (arguments.length == 0) {return curried;} //optimization, !essential
var newargs = Array.prototype.slice.call(args,0); // array or arguments
Array.prototype.push.apply(newargs,arguments); // someone was smart!
if (newargs.length >= func.length) {
return func.apply(this,newargs);
} else {
return curry(func,newargs);
}
}
}

It doesn't preserve the object that the original function is a method
of, since it can't know it. Should it remember some object that a
partially applied function is assigned to? I don't think so. As it is
now, you can assign a partially applied curried function as a method
and have it work correctly when you give it the remaining arguments
directly:

var o1 = new Object();
o1.foo = -1000000;
var o2 = new Object();
o2.foo = 42;
o1.cadd = curry(function(x,y,z){return x+y+z+this.foo});
o2.cadd13 = o1.cadd(1,3);
alert(o2.cadd13(-9));

The most meaningfull result of this code is that the this.foo
referst to o2, since cadd13 is a method of o2 when it is called.
If it had remembered that cadd was a method of o1, then it would
give a different, and IMO incorrect result.

It is easy for functions to curry themselves:

function add(x,y,z) {
if (arguments.length < add.length) {
return curry(add,arguments);
}
return x+y+z;
}

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?


That I hadn't testet the code. It works fine. :)
Does this refer to the above also?


Yep.
/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 20 '05 #18

Everyone who responded to this thread,

I feel really bad for having started a thread, and having generated so
many good responses, and then not even having time to fully look at the
answers (that isn't the sole justification of this thread of course!).

I find it interesting though, how an issue like currying can be applied in
a language like JS. Perhaps when winter time comes, I'll be posting
again... with more questions ;)

Regards,
Svend

(I've got to go read some psych bullshit now, if you'll excuse me ... )
Jul 20 '05 #19
Richard Cornford wrote:

<snip>

... it also occurred to me that the
function (func) itself may not require any parameters, which you have
covered here. However, if the function needs no parameters (func.length
== 0) shouldn't the value returned at this point be the result of
calling that function?


I considered the effect some months ago, but ultimately thought the
"desirable" behaviour may be unknowable. A curried funtion, pursuant to
the currying operation in javascript, has a function length property of
zero. If on account of that, currying a curried function returns the
curried function, then "curry" on a curried function results in the
identity transform.

In the version I propsosed, the same identity transform results from
calling a curried function with no parameters - it returned itself.

The question remaining is what should the curry operation, at the top
level, do with a function that has a zero length property? Options are
to return the function (my example), calling the function (your example)
or throw an exception that curry was attempted on a function expecting
no arguments.

Whilst I tend to the last option, it's not hard and fast. In an
reasonable program I would not expect the problem to arise, but am happy
to defer to any theoretician with a rule for what should happen.

kind regards,

Dom

Jul 20 '05 #20
Dom Leonard <do*************@senet.andthis.com.au> writes:
Richard Cornford wrote:
... it also occurred to me that the
function (func) itself may not require any parameters, which you have
covered here. However, if the function needs no parameters (func.length
== 0) shouldn't the value returned at this point be the result of
calling that function?


I considered the effect some months ago, but ultimately thought the
"desirable" behaviour may be unknowable.


In "normal" functional programming, the "type transformation"
associated with currying a function of n arguments is:

(Value^n -> Value) => Value->Value->....->Value -> Value
----------n times--------

The latter would be the type of a normal n-argument function in SML.

In SML, declared functions are normally curried.
The declaration
fun foo arg1 arg2 arg3 arg4 = ...
defines a curried function of four arguments, and has type
'a -> 'b -> 'c -> 'd -> ...

It is really a shorthand for four nested function expressions. If you
wrote it with only the anonymous function constructor, it would be

rec val foo = fn arg1 => fn arg2 => fn arg3 => fn arg4 => ...

The former type expression is the type of an SML function that takes
one argument which is an n-tuple in SML,
fun foo (arg1,arg2,arg3,arg4) = ...
or
rec val foo = fn (arg1,arg2,arg3,arg4) => ...
It is also the type of a normal Scheme function, since Scheme functions
are not curried by default.
(define (foo arg1 arg2 arg3 arg4) (...))
or
(define foo (lambda (arg1 arg2 arg3 arg4) (...)))
Even with that in mind, I think that the transformation of 0-ary
functions should still give a function, not call it. I see currying as
transforming one function into another with a different type. It never
turns a function into a non-function value, which calling the function
might.
A curried funtion, pursuant to the currying operation in javascript,
has a function length property of zero. If on account of that,
currying a curried function returns the curried function, then
"curry" on a curried function results in the identity transform.
I concur. That would be perfect.

In functional languages, a curried function is not nullary, but unary.
Currying an unary function often gives itself again.

In SML, currying doesn't need to be idempotent. If the original function
has type
(('a * 'b) * 'c) -> ...
then currying once gives
('a * 'b) -> 'c -> ...
which is the same as
('a * 'b) -> ('c -> ...)
This is a function with a product domain, so it can be curried again
to give
'a -> 'b -> 'c -> ...
In Javascript (or Scheme), we don't have tuples, so this case can't
occur.

The question remaining is what should the curry operation, at the top
level, do with a function that has a zero length property? Options are
to return the function (my example), calling the function (your
example) or throw an exception that curry was attempted on a function
expecting no arguments.
I vote for returning the function on the grounds that I think the
curring transformation is a function to function transformation. In
SML, a thunk (0-ary function) is really a unary function that takes a
0-ary tuple as argument.

However, looking just at the type transformation,
Value^0 -> Value
should become
Value
so there is some support for calling the function. It is also prettier
in other ways. A recursive definition of currying on types would be:

Curry (Value^{n+1)->Value) = Value -> (Curry (Value^n -> Value))
Curry (Value^0 -> Value) = Value

Hmm. I started out believeing that only returning the function itself
made sense, but I can see that calling it does have theoretical merit.
Whilst I tend to the last option, it's not hard and fast. In an
reasonable program I would not expect the problem to arise, but am
happy to defer to any theoretician with a rule for what should happen.


Theoretically, I would call the function. It is prettier and more consistent.
In practice, I want currying to be a function to function transformation.

/L 'yep, I'm a theoretician.'
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 20 '05 #21

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Christina | last post by:
How can my ASP serverscript read a JPG file and create a small thumbnail image? I don't mean simple resizing, but true resizing so that the image actually gets smaller in size. Is this possible...
7
by: nog | last post by:
What's the best approach to creating many objects of a class? I have in mind using something analogous to a table to hold the data - which is in a form similar to (char name, char address, date...
5
by: Chris | last post by:
Hi, to create an array of 2 objects (e.g. of type '__gc class Airplane') I need to do : Airplane * arrAirplanes __gc = new Airplane* __gc; arrAirplanes = new Airplane("N12344"); arrAirplanes...
9
by: Brian | last post by:
I have a question that some may consider silly, but it has me a bit stuck and I would appreciate some help in understanding what is going on. For example, lets say that I have a class that...
4
by: Amit Bhatia | last post by:
User-Agent: OSXnews 2.081 Xref: number1.nntp.dca.giganews.com comp.lang.c++:817424 Hi, I have posted this post also for the thread "vector of lists" but since it is about something else...
0
by: ayusmandash | last post by:
Is creating virtual serial port possible in C#? If yes how?
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.