N
NateTG
Fri, Jan 26, 2018 3:10 AM
I just implemented a 2-3 tree using OpenSCAD functions, and it seems pretty
ugly. I'm wondering if I'm doing something dumb. Here's the script:
twothreetree.scad http://forum.openscad.org/file/t2140/twothreetree.scad
--
Sent from: http://forum.openscad.org/
I just implemented a 2-3 tree using OpenSCAD functions, and it seems pretty
ugly. I'm wondering if I'm doing something dumb. Here's the script:
twothreetree.scad <http://forum.openscad.org/file/t2140/twothreetree.scad>
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Fri, Jan 26, 2018 10:21 AM
I have already pointed out that it is hard to write non-trivial data
structures in OpenSCAD. I see two main reasons for that: the only intrinsic
data structure in OpenSCAD is list (officially called vector) with very
basic operators and any change in a data structure implies in rewriting all
the structure because of the lack of variables and assignments in OpenSCAD.
Yes, I know that OpenSCAD is not a programming language.
A 2-3 tree is not a trivial data structure and I suspect its implementation
code would be ugly in any programming language. But I think you could
improve your code readability by providing a proper set of atomic functions
to operate over the subjacent node representation. So, your higher level
functions (for insertion for instance) would not be cluttered by operations
on the list components of tree nodes. As an example, the expression:
[node[1],[tree,node[1],[node[2],node[3],node[4]]]]
appearing somewhere in addtreetotreenode function definition seems to be
more readable (although not always concise) as something like:
let( lval = left_val(node), l2node = left_val(node) )
...
new_2_leaf( lval, new_2_node( tree, lval, l2node ) )
2018-01-26 1:10 GMT-02:00 NateTG nate-openscadforum@pedantic.org:
I have already pointed out that it is hard to write non-trivial data
structures in OpenSCAD. I see two main reasons for that: the only intrinsic
data structure in OpenSCAD is list (officially called vector) with very
basic operators and any change in a data structure implies in rewriting all
the structure because of the lack of variables and assignments in OpenSCAD.
Yes, I know that OpenSCAD is not a programming language.
A 2-3 tree is not a trivial data structure and I suspect its implementation
code would be ugly in any programming language. But I think you could
improve your code readability by providing a proper set of atomic functions
to operate over the subjacent node representation. So, your higher level
functions (for insertion for instance) would not be cluttered by operations
on the list components of tree nodes. As an example, the expression:
[node[1],[tree,node[1],[node[2],node[3],node[4]]]]
appearing somewhere in addtreetotreenode function definition seems to be
more readable (although not always concise) as something like:
let( lval = left_val(node), l2node = left_val(node) )
...
new_2_leaf( lval, new_2_node( tree, lval, l2node ) )
2018-01-26 1:10 GMT-02:00 NateTG <nate-openscadforum@pedantic.org>:
> I just implemented a 2-3 tree using OpenSCAD functions, and it seems pretty
> ugly. I'm wondering if I'm doing something dumb. Here's the script:
>
> twothreetree.scad <http://forum.openscad.org/file/t2140/twothreetree.scad>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
N
NateTG
Fri, Jan 26, 2018 3:35 PM
Thanks for looking at it. Reading other people's code isn't always the
easiest thing in the world. I'm still finding my feet in this purely
functional approach, so it's not surprising that it's ugly. (TBH, I'm a
little surprised at how easily I got it working.)
I have already pointed out that it is hard to write non-trivial data
structures in OpenSCAD. I see two main reasons for that: the only
intrinsic data structure in OpenSCAD is list (officially called vector)
with very basic operators and any change in a data structure implies in
rewriting all the structure because of the lack of variables and
assignments in OpenSCAD.
A specific question is whether my sense that adding or removing any data
from a structure is always going to involve reconstructing the insert or
remove path, and it seems like the answer to that is yes.
Do you know if OpenSCAD has garbage collection or some other memory
management scheme?
--
Sent from: http://forum.openscad.org/
Thanks for looking at it. Reading other people's code isn't always the
easiest thing in the world. I'm still finding my feet in this purely
functional approach, so it's not surprising that it's ugly. (TBH, I'm a
little surprised at how easily I got it working.)
> I have already pointed out that it is hard to write non-trivial data
> structures in OpenSCAD. I see two main reasons for that: the only
> intrinsic data structure in OpenSCAD is list (officially called vector)
> with very basic operators and any change in a data structure implies in
> rewriting all the structure because of the lack of variables and
> assignments in OpenSCAD.
A specific question is whether my sense that adding or removing any data
from a structure is always going to involve reconstructing the insert or
remove path, and it seems like the answer to that is yes.
Do you know if OpenSCAD has garbage collection or some other memory
management scheme?
--
Sent from: http://forum.openscad.org/
DM
doug moen
Fri, Jan 26, 2018 7:05 PM
As Ronaldo says, "OpenSCAD is not a programming language". It's missing
features that would make this kind of programming easier and more
comfortable. I don't like calling OpenSCAD a functional programming
language, because it's missing many of the features that you expect in a
functional language, such as function values.
OpenSCAD uses reference counting for memory management.
You wrote:
:(1==len(tree))?
(value==tree[0])?
true
:
false
You could make the code shorter by writing
:(1==len(tree))?
value==tree[0]
I don't claim this is easier to understand, but it it is shorter.
This kind of code is hard to read, because of all of the "magic number"
indexes:
[treenode[0],treenode[1],[treenode[2][0],treenode[2][1],treenode[2][2]],treenode[2][3],[treenode[2][4],treenode[3],treenode[4][1]]]
It would be better to use names, instead of numbers. Ronaldo suggests using
helper functions. I tend to use named constants as indexes into lists that
actually structures/records, kind of like this:
BRANCH1 = 0;
VALUE1 = 1;
BRANCH2 = 2;
VALUE2 = 3;
BRANCH3 = 4;
[ treenode[BRANCH1],
treenode[VALUE1],
[ treenode[BRANCH2][BRANCH1], treenode[BRANCH2][VALUE1],
treenode[BRANCH2][BRANCH2] ],
treenode[BRANCH2][VALUE2],
[ treenode[BRANCH2][BRANCH3], treenode[VALUE2], treenode[BRANCH3][VALUE1]
]
]
As Ronaldo says, "OpenSCAD is not a programming language". It's missing
features that would make this kind of programming easier and more
comfortable. I don't like calling OpenSCAD a functional programming
language, because it's missing many of the features that you expect in a
functional language, such as function values.
OpenSCAD uses reference counting for memory management.
You wrote:
:(1==len(tree))?
(value==tree[0])?
true
:
false
You could make the code shorter by writing
:(1==len(tree))?
value==tree[0]
I don't claim this is easier to understand, but it it is shorter.
This kind of code is hard to read, because of all of the "magic number"
indexes:
[treenode[0],treenode[1],[treenode[2][0],treenode[2][1],treenode[2][2]],treenode[2][3],[treenode[2][4],treenode[3],treenode[4][1]]]
It would be better to use names, instead of numbers. Ronaldo suggests using
helper functions. I tend to use named constants as indexes into lists that
actually structures/records, kind of like this:
BRANCH1 = 0;
VALUE1 = 1;
BRANCH2 = 2;
VALUE2 = 3;
BRANCH3 = 4;
[ treenode[BRANCH1],
treenode[VALUE1],
[ treenode[BRANCH2][BRANCH1], treenode[BRANCH2][VALUE1],
treenode[BRANCH2][BRANCH2] ],
treenode[BRANCH2][VALUE2],
[ treenode[BRANCH2][BRANCH3], treenode[VALUE2], treenode[BRANCH3][VALUE1]
]
]
RP
Ronaldo Persiano
Fri, Jan 26, 2018 11:21 PM
There is no information hiding resource in OpenSCAD. However, the coder can
avoid to mix lower and higher level operations by using proper atomic
functions for creation, inspecting and information retrieval of data nodes
virtually isolating its data representation from data structure operations.
I would not call those atomic functions as helper functions.
Nate's treefindvalue() function for instance might have the following much
more readable form:
function is_in_tree(tree, value) =
let( Lvalue = Lvalue(tree),
Rvalue = Rvalue(tree),
left = Lbranch(tree),
right = Rbranch(tree) )
is_2node(tree)?
( value==Lvalue )
|| ( (value<Lvalue) && is_in_tree(left, value) )
|| is_in_tree(right, value)
:is_3node(tree)?
(value==Lvalue)
|| ( value==Rvalue )
|| ( (value<Lvalue) && is_in_tree(left, value) )
|| ( (value>Rvalue) && is_in_tree(right, value) )
|| is_in_tree(Cbranch(tree), value)
:is_2leaf(tree)?
( value==Lvalue )
|| ( value==Rvalue )
:is_leaf(tree) && (value==Lvalue)
;
where a bunch of data representation access functions are called. To write
and read this code we don't need know where the left branch Lbranch() is
stored or how the test is_2node(tree) is performed. The code of is_in_tree
may be a bit less efficient than Nate's one but readability,
maintainability are prime qualities.
The algorithms for value insertion and removal from a 2-3 tree require the
manipulation of temporary abnormal nodes storing 3 values. So, the node
data representation should be able to store that kind of nodes. A set of
functions for creation, testing and value retrieval of temporary nodes may
be:
// temporary nodes
function tnode(v1,v2,v3, t1,t2,t3) = [t1, v1, t2, v2, t3, v3]; // creation
function is_temp(t) = (len(t)==6); // testing
function Tvalue(tree,ind) = t[2ind+1]; // retrieval
function Tbranch(tree,ind) = t[2ind];
By calling those functions, the value insertion coding will be easier and
the code clearer.
2-3 tree is a data abstraction for the user code. The node might be
regarded as a data abstraction for the 2-3 tree data structure.
2018-01-26 17:05 GMT-02:00 doug moen doug@moens.org:
As Ronaldo says, "OpenSCAD is not a programming language". It's missing
features that would make this kind of programming easier and more
comfortable. I don't like calling OpenSCAD a functional programming
language, because it's missing many of the features that you expect in a
functional language, such as function values.
OpenSCAD uses reference counting for memory management.
You wrote:
:(1==len(tree))?
(value==tree[0])?
true
:
false
You could make the code shorter by writing
:(1==len(tree))?
value==tree[0]
I don't claim this is easier to understand, but it it is shorter.
This kind of code is hard to read, because of all of the "magic number"
indexes:
[treenode[0],treenode[1],[treenode[2][0],treenode[2][1],treenode[2][2]],treenode[2][3],[treenode[2][4],treenode[3],treenode[4][1]]]
It would be better to use names, instead of numbers. Ronaldo suggests
using helper functions. I tend to use named constants as indexes into lists
that actually structures/records, kind of like this:
BRANCH1 = 0;
VALUE1 = 1;
BRANCH2 = 2;
VALUE2 = 3;
BRANCH3 = 4;
[ treenode[BRANCH1],
treenode[VALUE1],
[ treenode[BRANCH2][BRANCH1], treenode[BRANCH2][VALUE1],
treenode[BRANCH2][BRANCH2] ],
treenode[BRANCH2][VALUE2],
[ treenode[BRANCH2][BRANCH3], treenode[VALUE2],
treenode[BRANCH3][VALUE1] ]
]
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
There is no information hiding resource in OpenSCAD. However, the coder can
avoid to mix lower and higher level operations by using proper atomic
functions for creation, inspecting and information retrieval of data nodes
virtually isolating its data representation from data structure operations.
I would not call those atomic functions as helper functions.
Nate's treefindvalue() function for instance might have the following much
more readable form:
function is_in_tree(tree, value) =
let( Lvalue = Lvalue(tree),
Rvalue = Rvalue(tree),
left = Lbranch(tree),
right = Rbranch(tree) )
is_2node(tree)?
( value==Lvalue )
|| ( (value<Lvalue) && is_in_tree(left, value) )
|| is_in_tree(right, value)
:is_3node(tree)?
(value==Lvalue)
|| ( value==Rvalue )
|| ( (value<Lvalue) && is_in_tree(left, value) )
|| ( (value>Rvalue) && is_in_tree(right, value) )
|| is_in_tree(Cbranch(tree), value)
:is_2leaf(tree)?
( value==Lvalue )
|| ( value==Rvalue )
:is_leaf(tree) && (value==Lvalue)
;
where a bunch of data representation access functions are called. To write
and read this code we don't need know where the left branch Lbranch() is
stored or how the test is_2node(tree) is performed. The code of is_in_tree
may be a bit less efficient than Nate's one but readability,
maintainability are prime qualities.
The algorithms for value insertion and removal from a 2-3 tree require the
manipulation of temporary abnormal nodes storing 3 values. So, the node
data representation should be able to store that kind of nodes. A set of
functions for creation, testing and value retrieval of temporary nodes may
be:
// temporary nodes
function tnode(v1,v2,v3, t1,t2,t3) = [t1, v1, t2, v2, t3, v3]; // creation
function is_temp(t) = (len(t)==6); // testing
function Tvalue(tree,ind) = t[2*ind+1]; // retrieval
function Tbranch(tree,ind) = t[2*ind];
By calling those functions, the value insertion coding will be easier and
the code clearer.
2-3 tree is a data abstraction for the user code. The node might be
regarded as a data abstraction for the 2-3 tree data structure.
2018-01-26 17:05 GMT-02:00 doug moen <doug@moens.org>:
> As Ronaldo says, "OpenSCAD is not a programming language". It's missing
> features that would make this kind of programming easier and more
> comfortable. I don't like calling OpenSCAD a functional programming
> language, because it's missing many of the features that you expect in a
> functional language, such as function values.
>
> OpenSCAD uses reference counting for memory management.
>
>
> You wrote:
>
> :(1==len(tree))?
> (value==tree[0])?
> true
> :
> false
>
> You could make the code shorter by writing
>
> :(1==len(tree))?
> value==tree[0]
>
> I don't claim this is easier to understand, but it it is shorter.
>
>
> This kind of code is hard to read, because of all of the "magic number"
> indexes:
>
> [treenode[0],treenode[1],[treenode[2][0],treenode[2][1],treenode[2][2]],treenode[2][3],[treenode[2][4],treenode[3],treenode[4][1]]]
>
> It would be better to use names, instead of numbers. Ronaldo suggests
> using helper functions. I tend to use named constants as indexes into lists
> that actually structures/records, kind of like this:
>
> BRANCH1 = 0;
> VALUE1 = 1;
> BRANCH2 = 2;
> VALUE2 = 3;
> BRANCH3 = 4;
>
> [ treenode[BRANCH1],
> treenode[VALUE1],
> [ treenode[BRANCH2][BRANCH1], treenode[BRANCH2][VALUE1],
> treenode[BRANCH2][BRANCH2] ],
> treenode[BRANCH2][VALUE2],
> [ treenode[BRANCH2][BRANCH3], treenode[VALUE2],
> treenode[BRANCH3][VALUE1] ]
> ]
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
T
TLC123
Sat, Jan 27, 2018 5:18 AM
Those named indexes is a neat style .
Paired with some selected glue comments it has potential.
And with a helper constructor function it hides the "listness" of the data.
I like it.
// NamedStruct "2-3TreeNode"
BRANCH1 = 0; // pointer
VALUE1 = 1; // value
BRANCH2 = 2; // pointer
VALUE2 = 3; // value
BRANCH3 = 4; // pointer
//
function new_2-3TreeNode ( BRANCH1, VALUE1 , BRANCH2, VALUE2, BRANCH3)=
[BRANCH1, VALUE1 , BRANCH2, VALUE2,
BRANCH3];
--
Sent from: http://forum.openscad.org/
Those named indexes is a neat style .
Paired with some selected glue comments it has potential.
And with a helper constructor function it hides the "listness" of the data.
I like it.
// NamedStruct "2-3TreeNode"
BRANCH1 = 0; // pointer
VALUE1 = 1; // value
BRANCH2 = 2; // pointer
VALUE2 = 3; // value
BRANCH3 = 4; // pointer
//
function new_2-3TreeNode ( BRANCH1, VALUE1 , BRANCH2, VALUE2, BRANCH3)=
[BRANCH1, VALUE1 , BRANCH2, VALUE2,
BRANCH3];
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Tue, Jan 30, 2018 9:54 PM
A specific question is whether my sense that adding or removing any data
from a structure is always going to involve reconstructing the insert or
remove path, and it seems like the answer to that is yes.
Do you know if OpenSCAD has garbage collection or some other memory
management scheme?
My greatest concerns on defining data structures in OpenSCAD language are
not the lacking of a garbage collection but the order of complexity. For
instance, the element insertion and removing from 2-3 trees have order
O(log n). However, an analysis of your implementation in OpenSCAD will show
that its order of complexity is O(n2) or worst due to the repeated structure
copies required by any change in it. And this is true for any data
structure.
So, I think there is no point in using complex data structures in OpenSCAD.
A simple ordered list is as good as any search tree.
2018-01-26 13:35 GMT-02:00 NateTG <nate-openscadforum@pedantic.org>:
>
> A specific question is whether my sense that adding or removing any data
> from a structure is always going to involve reconstructing the insert or
> remove path, and it seems like the answer to that is yes.
>
> Do you know if OpenSCAD has garbage collection or some other memory
> management scheme?
My greatest concerns on defining data structures in OpenSCAD language are
not the lacking of a garbage collection but the order of complexity. For
instance, the element insertion and removing from 2-3 trees have order
O(log n). However, an analysis of your implementation in OpenSCAD will show
that its order of complexity is O(n2) or worst due to the repeated structure
copies required by any change in it. And this is true for any data
structure.
So, I think there is no point in using complex data structures in OpenSCAD.
A simple ordered list is as good as any search tree.
N
NateTG
Tue, Jan 30, 2018 11:05 PM
... For instance, the element insertion and removing from 2-3 trees have
order O(log n). However, an analysis of your implementation in OpenSCAD will
show that its order of complexity is O(n2) or worst due to the repeated
structure copies required by any change in it. ...
Does OpenSCAD never use references (so that if there's some variable a, then
b=[a] is going to create a copy of it)?
--
Sent from: http://forum.openscad.org/
> ... For instance, the element insertion and removing from 2-3 trees have
order O(log n). However, an analysis of your implementation in OpenSCAD will
show that its order of complexity is O(n2) or worst due to the repeated
structure copies required by any change in it. ...
Does OpenSCAD never use references (so that if there's some variable a, then
b=[a] is going to create a copy of it)?
--
Sent from: http://forum.openscad.org/
DM
doug moen
Tue, Jan 30, 2018 11:16 PM
OpenSCAD uses references to reference-counted, immutable values. Multiple
variables can point to the same immutable list object. There is no need for
OpenSCAD to make copies of lists, since there are no operations for
mutating a list. AFAIK; I haven't read all the code in the interpreter.
I don't think there's much point in worrying about this, though. Like
Ronaldo says, just use the simplest possible data structure. If this leads
to a performance problem, and you want to use a more complicated data
structure, you should measure the performance to ensure that the more
complicated code is actually faster. OpenSCAD doesn't have the same
performance characteristics as conventional languages, so your complicated
code might be slower.
On 30 January 2018 at 18:05, NateTG nate-openscadforum@pedantic.org wrote:
... For instance, the element insertion and removing from 2-3 trees
OpenSCAD uses references to reference-counted, immutable values. Multiple
variables can point to the same immutable list object. There is no need for
OpenSCAD to make copies of lists, since there are no operations for
mutating a list. AFAIK; I haven't read all the code in the interpreter.
I don't think there's much point in worrying about this, though. Like
Ronaldo says, just use the simplest possible data structure. If this leads
to a performance problem, and you want to use a more complicated data
structure, you should measure the performance to ensure that the more
complicated code is actually faster. OpenSCAD doesn't have the same
performance characteristics as conventional languages, so your complicated
code might be slower.
On 30 January 2018 at 18:05, NateTG <nate-openscadforum@pedantic.org> wrote:
> > ... For instance, the element insertion and removing from 2-3 trees
> have
> order O(log n). However, an analysis of your implementation in OpenSCAD
> will
> show that its order of complexity is O(n2) or worst due to the repeated
> structure copies required by any change in it. ...
>
> Does OpenSCAD never use references (so that if there's some variable a,
> then
> b=[a] is going to create a copy of it)?
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RP
Ronaldo Persiano
Tue, Jan 30, 2018 11:22 PM
The question is how to build things like Delaunay triangulation when
needed. You can't do it without the support of a complex data structure.
2018-01-30 21:16 GMT-02:00 doug moen doug@moens.org:
OpenSCAD uses references to reference-counted, immutable values. Multiple
variables can point to the same immutable list object. There is no need for
OpenSCAD to make copies of lists, since there are no operations for
mutating a list. AFAIK; I haven't read all the code in the interpreter.
I don't think there's much point in worrying about this, though. Like
Ronaldo says, just use the simplest possible data structure. If this leads
to a performance problem, and you want to use a more complicated data
structure, you should measure the performance to ensure that the more
complicated code is actually faster. OpenSCAD doesn't have the same
performance characteristics as conventional languages, so your complicated
code might be slower.
The question is how to build things like Delaunay triangulation when
needed. You can't do it without the support of a complex data structure.
2018-01-30 21:16 GMT-02:00 doug moen <doug@moens.org>:
> OpenSCAD uses references to reference-counted, immutable values. Multiple
> variables can point to the same immutable list object. There is no need for
> OpenSCAD to make copies of lists, since there are no operations for
> mutating a list. AFAIK; I haven't read all the code in the interpreter.
>
> I don't think there's much point in worrying about this, though. Like
> Ronaldo says, just use the simplest possible data structure. If this leads
> to a performance problem, and you want to use a more complicated data
> structure, you should measure the performance to ensure that the more
> complicated code is actually faster. OpenSCAD doesn't have the same
> performance characteristics as conventional languages, so your complicated
> code might be slower.
>
DM
doug moen
Tue, Jan 30, 2018 11:29 PM
It may be possible to use some purely functional data structures in
OpenSCAD. Unlike the 2-3 tree, these data structures are designed to be
efficient in a language with only immutable objects.
https://en.wikipedia.org/wiki/Purely_functional_data_structure
OpenSCAD is missing some language features needed by some functional data
structures, but maybe this is a starting point.
On 30 January 2018 at 18:22, Ronaldo Persiano rcmpersiano@gmail.com wrote:
The question is how to build things like Delaunay triangulation when
needed. You can't do it without the support of a complex data structure.
2018-01-30 21:16 GMT-02:00 doug moen doug@moens.org:
OpenSCAD uses references to reference-counted, immutable values. Multiple
variables can point to the same immutable list object. There is no need for
OpenSCAD to make copies of lists, since there are no operations for
mutating a list. AFAIK; I haven't read all the code in the interpreter.
I don't think there's much point in worrying about this, though. Like
Ronaldo says, just use the simplest possible data structure. If this leads
to a performance problem, and you want to use a more complicated data
structure, you should measure the performance to ensure that the more
complicated code is actually faster. OpenSCAD doesn't have the same
performance characteristics as conventional languages, so your complicated
code might be slower.
It may be possible to use some purely functional data structures in
OpenSCAD. Unlike the 2-3 tree, these data structures are designed to be
efficient in a language with only immutable objects.
https://en.wikipedia.org/wiki/Purely_functional_data_structure
OpenSCAD is missing some language features needed by some functional data
structures, but maybe this is a starting point.
On 30 January 2018 at 18:22, Ronaldo Persiano <rcmpersiano@gmail.com> wrote:
> The question is how to build things like Delaunay triangulation when
> needed. You can't do it without the support of a complex data structure.
>
> 2018-01-30 21:16 GMT-02:00 doug moen <doug@moens.org>:
>
>> OpenSCAD uses references to reference-counted, immutable values. Multiple
>> variables can point to the same immutable list object. There is no need for
>> OpenSCAD to make copies of lists, since there are no operations for
>> mutating a list. AFAIK; I haven't read all the code in the interpreter.
>>
>> I don't think there's much point in worrying about this, though. Like
>> Ronaldo says, just use the simplest possible data structure. If this leads
>> to a performance problem, and you want to use a more complicated data
>> structure, you should measure the performance to ensure that the more
>> complicated code is actually faster. OpenSCAD doesn't have the same
>> performance characteristics as conventional languages, so your complicated
>> code might be slower.
>>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
RP
Ronaldo Persiano
Wed, Jan 31, 2018 10:46 AM
Under my initial hyphotesis that copies are made, an insertion or deletion
from a 2-3 tree with N element would be O(N). And to build a tree from
scratch the time would be O(N^2). It is hard to get good time estimates
with the OpenSCAD system but I got worst than linear time building trees by
insertion of 2500-30000 elements.
To minimize stack overflows I have used the tail recursive versions:
function treeinsert(tree,value)=
(0==len(tree))?
[value] :
(treeinsertiterate(tree,value))[1] ;
function treeinsertlisti(tr,list,i)=
(i==0)?
treeinsert(tr,list[0]) :
treeinsertlisti(treeinsert(tr,list[i]),list,i-1) ;
BTW, your insertion code does not deal well with some sequences having
repeated values.
ECHO: foo = [40, 57, 65, 14, 65, 80, 82, 65]
ECHO: bar = [[14, 40], 57, [65], 80, [65]]
2018-01-30 21:59 GMT-02:00 NateTG nate-openscadforum@pedantic.org:
OpenSCAD uses references to reference-counted, immutable values. Multiple
variables can point to the same immutable list object. There is no need for
OpenSCAD to make copies of lists, since there are no operations for
mutating
a list. AFAIK; I haven't read all the code in the interpreter.
The 2-3 tree implementation should only replace the nodes on the insertion
or removal path which should individually take a fixed amount of time. I
don't understand how it will take O( N^2 ) time and would really like to
understand. (Even if OpenSCAD is copying the lists every time, it seems
like it should be at worst O(N (log N)^2).)
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Under my initial hyphotesis that copies are made, an insertion or deletion
from a 2-3 tree with N element would be O(N). And to build a tree from
scratch the time would be O(N^2). It is hard to get good time estimates
with the OpenSCAD system but I got worst than linear time building trees by
insertion of 2500-30000 elements.
To minimize stack overflows I have used the tail recursive versions:
function treeinsert(tree,value)=
(0==len(tree))?
[value] :
(treeinsertiterate(tree,value))[1] ;
function treeinsertlisti(tr,list,i)=
(i==0)?
treeinsert(tr,list[0]) :
treeinsertlisti(treeinsert(tr,list[i]),list,i-1) ;
BTW, your insertion code does not deal well with some sequences having
repeated values.
ECHO: foo = [40, 57, 65, 14, 65, 80, 82, 65]
ECHO: bar = [[14, 40], 57, [65], 80, [65]]
2018-01-30 21:59 GMT-02:00 NateTG <nate-openscadforum@pedantic.org>:
> > OpenSCAD uses references to reference-counted, immutable values. Multiple
> variables can point to the same immutable list object. There is no need for
> OpenSCAD to make copies of lists, since there are no operations for
> mutating
> a list. AFAIK; I haven't read all the code in the interpreter.
>
> The 2-3 tree implementation should only replace the nodes on the insertion
> or removal path which should individually take a fixed amount of time. I
> don't understand how it will take O( N^2 ) time and would really like to
> understand. (Even if OpenSCAD is copying the lists every time, it seems
> like it should be at worst O(N (log N)^2).)
>
>
>
>
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
N
NateTG
Wed, Jan 31, 2018 2:30 PM
Under my initial hyphotesis that copies are made, an insertion or deletion
from a 2-3 tree with N element would be O(N). And to build a tree from
scratch the time would be O(N^2).
Those are both totally plausible to me. I may have misunderstood this:
For instance, the element insertion and removing from 2-3 trees have
order O(log n). However, an analysis of your implementation in OpenSCAD
will show that its order of complexity is O(n2) or worst due to the
repeated structure copies required by any change in it.
Where I thought the claim was that a single insertion was O(N^2).
I got worst than linear time building trees by insertion of 2500-30000
elements.
Building a tree from N elements is expected to take O(N log N) time - which
is worse than linear.
To minimize stack overflows I have used the tail recursive versions:
...
BTW, your insertion code does not deal well with some sequences having
repeated values.
> Under my initial hyphotesis that copies are made, an insertion or deletion
from a 2-3 tree with N element would be O(N). And to build a tree from
scratch the time would be O(N^2).
Those are both totally plausible to me. I may have misunderstood this:
> For instance, the element insertion and removing from 2-3 trees have
> order O(log n). However, an analysis of your implementation in OpenSCAD
> will show that its order of complexity is O(n2) or worst due to the
> repeated structure copies required by any change in it.
Where I thought the claim was that a single insertion was O(N^2).
> I got worst than linear time building trees by insertion of 2500-30000
> elements.
Building a tree from N elements is expected to take O(N log N) time - which
is worse than linear.
> To minimize stack overflows I have used the tail recursive versions:
> ...
> BTW, your insertion code does not deal well with some sequences having
> repeated values.
Thanks for that info. I still don't really understand the "this tail
recurses" thing.
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Wed, Jan 31, 2018 3:31 PM
Where I thought the claim was that a single insertion was O(N^2).
Yes, I mixed things up.
I still don't really understand the "this tail
>
> Where I thought the claim was that a single insertion was O(N^2).
Yes, I mixed things up.
I still don't really understand the "this tail
>
> recurses" thing.
>
<goog_634797904>
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/User-Defined_Functions_and_Modules#Recursive_functions
*http://forum.openscad.org/Tail-recursion-td17040.html*
<http://forum.openscad.org/Tail-recursion-td17040.html>
TG
Tony Godshall
Wed, Jan 31, 2018 6:09 PM
I still don't really understand the "this tail
recurses" thing.
From a language user point of view, it can be ignored.
From an optimization point of view, conventional calls
and returns stack up and grow the stack, but a tail return
can avoid putting a redundant return on the stack.
Another way to think about it is a recursive call can be
a goto, and not grow the stack.
--
Best Regards.
This is unedited.
This message came out of me
via a suboptimal keyboard.
>> I still don't really understand the "this tail
>> recurses" thing.
>From a language user point of view, it can be ignored.
>From an optimization point of view, conventional calls
and returns stack up and grow the stack, but a tail return
can avoid putting a redundant return on the stack.
Another way to think about it is a recursive call can be
a goto, and not grow the stack.
--
--
Best Regards.
This is unedited.
This message came out of me
via a suboptimal keyboard.
N
NateTG
Wed, Jan 31, 2018 6:22 PM
I understand what tail recursion is. I'm just not confident about when or how
it gets applied in OpenSCAD.
From a language user point of view, it can be ignored.
It would be nice if that were true, but the nature of OpenSCAD means that
things can easily get stack constrained. even when they are working
properly. For example, Ronaldo didn't modify the tree insertion function
for some aesthetic reason but rather because stack overflows were a
practical issue.
--
Sent from: http://forum.openscad.org/
I understand what tail recursion is. I'm just not confident about when or how
it gets applied in OpenSCAD.
> From a language user point of view, it can be ignored.
It would be nice if that were true, but the nature of OpenSCAD means that
things can easily get stack constrained. even when they are working
properly. For example, Ronaldo didn't modify the tree insertion function
for some aesthetic reason but rather because stack overflows were a
practical issue.
--
Sent from: http://forum.openscad.org/
DM
doug moen
Wed, Jan 31, 2018 6:33 PM
"I understand what tail recursion is. I'm just not confident about when or
how
it gets applied in OpenSCAD."
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
The first case:
function f(x) = shouldExit ? result : f(...);
Note that there is a tail recursive function call after the :
The second case:
function f(x) = keepGoing ? f(...) : result;
Note that there is a tail recursive function call between ? and :
The body of the function must match one of the two patterns. If you change
these patterns in any way (eg, add a 'let' to define local variables), then
the pattern no longer matches, and tail recursion optimization is not
applied.
On 31 January 2018 at 13:22, NateTG nate-openscadforum@pedantic.org wrote:
I understand what tail recursion is. I'm just not confident about when or
how
it gets applied in OpenSCAD.
From a language user point of view, it can be ignored.
"I understand what tail recursion is. I'm just not confident about when or
how
it gets applied in OpenSCAD."
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
The first case:
function f(x) = shouldExit ? result : f(...);
Note that there is a tail recursive function call after the :
The second case:
function f(x) = keepGoing ? f(...) : result;
Note that there is a tail recursive function call between ? and :
The body of the function must match one of the two patterns. If you change
these patterns in any way (eg, add a 'let' to define local variables), then
the pattern no longer matches, and tail recursion optimization is not
applied.
On 31 January 2018 at 13:22, NateTG <nate-openscadforum@pedantic.org> wrote:
> I understand what tail recursion is. I'm just not confident about when or
> how
> it gets applied in OpenSCAD.
>
> > From a language user point of view, it can be ignored.
>
> It would be nice if that were true, but the nature of OpenSCAD means that
> things can easily get stack constrained. even when they are working
> properly. For example, Ronaldo didn't modify the tree insertion function
> for some aesthetic reason but rather because stack overflows were a
> practical issue.
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RP
Ronaldo Persiano
Wed, Jan 31, 2018 7:56 PM
Not always it is easy to write a tail recursion qualified to elimination.
And sometimes it is possible to avoid the recursion at all using a C-like
for enabled in a snapshot version. The treeinsertlist(() function, for
instance, may be rewritten iteractively as:
function treeinsertlist(tree,list) =
[for(i=len(list), tr=tree; i>=0; i=i-1, tr = treeinsert(tr,list[i]) )
if(i==0) tr ][0];
I am not an advocate of this form that is not easily grasped, but a complex
tail recursion elimination form may be also hard to read.
2018-01-31 16:33 GMT-02:00 doug moen doug@moens.org:
"I understand what tail recursion is. I'm just not confident about when
or how
it gets applied in OpenSCAD."
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
The first case:
function f(x) = shouldExit ? result : f(...);
Note that there is a tail recursive function call after the :
The second case:
function f(x) = keepGoing ? f(...) : result;
Note that there is a tail recursive function call between ? and :
The body of the function must match one of the two patterns. If you change
these patterns in any way (eg, add a 'let' to define local variables), then
the pattern no longer matches, and tail recursion optimization is not
applied.
On 31 January 2018 at 13:22, NateTG nate-openscadforum@pedantic.org
wrote:
I understand what tail recursion is. I'm just not confident about when or
how
it gets applied in OpenSCAD.
From a language user point of view, it can be ignored.
Not always it is easy to write a tail recursion qualified to elimination.
And sometimes it is possible to avoid the recursion at all using a C-like
*for* enabled in a snapshot version. The treeinsertlist(() function, for
instance, may be rewritten iteractively as:
function treeinsertlist(tree,list) =
[for(i=len(list), tr=tree; i>=0; i=i-1, tr = treeinsert(tr,list[i]) )
if(i==0) tr ][0];
I am not an advocate of this form that is not easily grasped, but a complex
tail recursion elimination form may be also hard to read.
2018-01-31 16:33 GMT-02:00 doug moen <doug@moens.org>:
> "I understand what tail recursion is. I'm just not confident about when
> or how
> it gets applied in OpenSCAD."
>
> The tail recursion optimization is only applied in two very specific
> circumstances. It is not implemented in a general way.
>
> The first case:
>
> function f(x) = shouldExit ? result : f(...);
>
> Note that there is a tail recursive function call after the :
>
> The second case:
>
> function f(x) = keepGoing ? f(...) : result;
>
> Note that there is a tail recursive function call between ? and :
>
> The body of the function must match one of the two patterns. If you change
> these patterns in any way (eg, add a 'let' to define local variables), then
> the pattern no longer matches, and tail recursion optimization is not
> applied.
>
> On 31 January 2018 at 13:22, NateTG <nate-openscadforum@pedantic.org>
> wrote:
>
>> I understand what tail recursion is. I'm just not confident about when or
>> how
>> it gets applied in OpenSCAD.
>>
>> > From a language user point of view, it can be ignored.
>>
>> It would be nice if that were true, but the nature of OpenSCAD means that
>> things can easily get stack constrained. even when they are working
>> properly. For example, Ronaldo didn't modify the tree insertion function
>> for some aesthetic reason but rather because stack overflows were a
>> practical issue.
>>
>>
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
TP
Torsten Paul
Wed, Jan 31, 2018 7:57 PM
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
Do you have any pointers to documentation about how to implement tail
recursion or how it's implemented in some language?
I was searching for that some time ago and there's lots of discussion
about how to use tail recursion but not so much about how to actually
implement it.
ciao,
Torsten.
> The tail recursion optimization is only applied in two very specific
> circumstances. It is not implemented in a general way.
>
Do you have any pointers to documentation about how to implement tail
recursion or how it's implemented in some language?
I was searching for that some time ago and there's lots of discussion
about how to use tail recursion but not so much about how to actually
implement it.
ciao,
Torsten.
TG
Tony Godshall
Wed, Jan 31, 2018 9:12 PM
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
--
Best Regards.
This is unedited.
This message came out of me
via a suboptimal keyboard.
https://en.wikipedia.org/wiki/Tail_call#cite_note-aim-443-1
The rest of the footnotes there are also good.
On Wed, Jan 31, 2018 at 11:57 AM, Torsten Paul <Torsten.Paul@gmx.de> wrote:
>> The tail recursion optimization is only applied in two very specific
>> circumstances. It is not implemented in a general way.
>>
> Do you have any pointers to documentation about how to implement tail
> recursion or how it's implemented in some language?
>
> I was searching for that some time ago and there's lots of discussion
> about how to use tail recursion but not so much about how to actually
> implement it.
>
> ciao,
> Torsten.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
--
Best Regards.
This is unedited.
This message came out of me
via a suboptimal keyboard.
DM
doug moen
Wed, Jan 31, 2018 9:19 PM
In order to implement generalized tail recursion optimization, you need to
add a compilation pass that compiles the code into an executable form that
is different from the original parse tree. This executable form is a
sequence of instructions. These are traditionally byte codes, but you could
also use a linked list of instruction objects, or an array of pointers to
instruction objects. This is like compiling into abstract machine code.
The interpreter uses a separate stack data structure to store argument
values and local variables for each function call. A linked list of
dynamically allocated stack frame objects is one possibility.
The interpreter is a state machine with a set of registers. You need at
least: a current instruction pointer IP, a current stack frame pointer FP.
After each instruction is executed, these registers are updated. The IP is
updated to the next instruction to be executed. The FP is updated after a
call or return.
Instructions need operands: some way to access the data that is to be
operated on. In a parse tree interpreter, like what OpenSCAD uses, a tree
node like "+" contains child nodes for the left and right operands of "+".
However, in this style of interpreter, an expression like 'A+B' is compiled
into:
evaluate A, store the result
evaluate B, store the result
fetch the A result, fetch the B result, add them, store the result
There are two basic styles that can be used for fetching and storing data:
-
One is a "stack machine", where you are pushing and popping intermediate
results on the stack. In this style, 'A+B' compiles into:
- push A on the stack
- push B on the stack
- pop 2 values from the stack, add them, push the result on the stack
-
Another style is a "register machine", where there is an array of slots
indexed by integers that hold values. Each operation has slot indexes
indicating which slots it should load and store data from. Register
machines are currently more popular. It seems you can generate faster code,
and they aren't any more difficult to implement. Even with a stack machine,
you need a slot array to store local variables.
A?B:C expressions are compiled into an instruction sequence that contains
conditional and unconditional jumps. Like machine code.
In Curv, my stack is a linked list of dynamically allocated stack frame
objects. Each frame contains an array of slots. The number of slots for a
particular function call frame is determined by the compiler. A function
object contains the number of slots needed for a call to that function.
To compile a function call, you first emit instructions to evaluate the
arguments (which are either pushed on the stack, or stored in one or more
slots), then you emit a function-call instruction, which allocates stack
space for the function call, possibly copies the arguments into the new
stack frame, depending on your design, then it sets the FP to point to the
new stack frame and it sets the IP to point to the first instruction of the
function. The old IP needs to be stored somewhere, eg in the return address
field of the new stack frame, so that it can be restored by the return
instruction.
To return from a function call, you execute a return instruction. This
takes the return value and places it somewhere where it can be found by the
function-call instruction that called the current function. You need to
restore the FP to its previous value (pop the current frame off the stack),
and set the IP to the next instruction following the original function
call. Deallocate the current frame, which is no longer needed.
There is also a tail-call instruction. In the general case, you can perform
a tail call to the same function, or to a different function. Suppose that
your stack is a linked list of dynamically allocated frame objects. A
general tail call instruction will create and initialize the new stack
frame, just like regular function call. But then it will set the return
address field of the new stack frame to the same value as the return
address of the current stack frame. Then it destroys the current stack
frame, replacing it with the new stack frame.
Now, your compiler needs a strategy for figuring out when it can safely
replace a regular function-call instruction with a tail-call instruction.
- The body of a function is in tail-position.
- If a let expression is in tail-position, then the body of the let is also
in tail-position.
- If A?B:C is in tail position, then B and C are also in tail-position.
- These rules for marking an expression as "in tail-position" are applied
recursively to the parse tree of a function body.
- If a function call is in tail position, then it can be compiled using a
tail-call instruction.
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I compile
variable and argument references into slot indexes, which index into the
slot array of the current stack frame. So the scope of a variable name is
fixed at compile time, before the program is evaluated. This is probably
why the Curv interpreter is so much faster than the OpenSCAD interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD is
dynamically scoped. Variable and argument references are names, and these
names are looked up dynamically during evaluation, which leads to semantics
that are different from lexical scoping. The same variable name node in the
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and I'm
not sure there are any references to help you, because the dynamic scoping
semantics of OpenSCAD seem utterly unique to me.
On 31 January 2018 at 14:57, Torsten Paul Torsten.Paul@gmx.de wrote:
The tail recursion optimization is only applied in two very specific
circumstances. It is not implemented in a general way.
Do you have any pointers to documentation about how to implement tail
In order to implement generalized tail recursion optimization, you need to
add a compilation pass that compiles the code into an executable form that
is different from the original parse tree. This executable form is a
sequence of instructions. These are traditionally byte codes, but you could
also use a linked list of instruction objects, or an array of pointers to
instruction objects. This is like compiling into abstract machine code.
The interpreter uses a separate stack data structure to store argument
values and local variables for each function call. A linked list of
dynamically allocated stack frame objects is one possibility.
The interpreter is a state machine with a set of registers. You need at
least: a current instruction pointer IP, a current stack frame pointer FP.
After each instruction is executed, these registers are updated. The IP is
updated to the next instruction to be executed. The FP is updated after a
call or return.
Instructions need operands: some way to access the data that is to be
operated on. In a parse tree interpreter, like what OpenSCAD uses, a tree
node like "+" contains child nodes for the left and right operands of "+".
However, in this style of interpreter, an expression like 'A+B' is compiled
into:
evaluate A, store the result
evaluate B, store the result
fetch the A result, fetch the B result, add them, store the result
There are two basic styles that can be used for fetching and storing data:
1. One is a "stack machine", where you are pushing and popping intermediate
results on the stack. In this style, 'A+B' compiles into:
* push A on the stack
* push B on the stack
* pop 2 values from the stack, add them, push the result on the stack
2. Another style is a "register machine", where there is an array of slots
indexed by integers that hold values. Each operation has slot indexes
indicating which slots it should load and store data from. Register
machines are currently more popular. It seems you can generate faster code,
and they aren't any more difficult to implement. Even with a stack machine,
you need a slot array to store local variables.
A?B:C expressions are compiled into an instruction sequence that contains
conditional and unconditional jumps. Like machine code.
In Curv, my stack is a linked list of dynamically allocated stack frame
objects. Each frame contains an array of slots. The number of slots for a
particular function call frame is determined by the compiler. A function
object contains the number of slots needed for a call to that function.
To compile a function call, you first emit instructions to evaluate the
arguments (which are either pushed on the stack, or stored in one or more
slots), then you emit a function-call instruction, which allocates stack
space for the function call, possibly copies the arguments into the new
stack frame, depending on your design, then it sets the FP to point to the
new stack frame and it sets the IP to point to the first instruction of the
function. The old IP needs to be stored somewhere, eg in the return address
field of the new stack frame, so that it can be restored by the return
instruction.
To return from a function call, you execute a return instruction. This
takes the return value and places it somewhere where it can be found by the
function-call instruction that called the current function. You need to
restore the FP to its previous value (pop the current frame off the stack),
and set the IP to the next instruction following the original function
call. Deallocate the current frame, which is no longer needed.
There is also a tail-call instruction. In the general case, you can perform
a tail call to the same function, or to a different function. Suppose that
your stack is a linked list of dynamically allocated frame objects. A
general tail call instruction will create and initialize the new stack
frame, just like regular function call. But then it will set the return
address field of the new stack frame to the same value as the return
address of the current stack frame. Then it destroys the current stack
frame, replacing it with the new stack frame.
Now, your compiler needs a strategy for figuring out when it can safely
replace a regular function-call instruction with a tail-call instruction.
* The body of a function is in tail-position.
* If a let expression is in tail-position, then the body of the let is also
in tail-position.
* If A?B:C is in tail position, then B and C are also in tail-position.
* These rules for marking an expression as "in tail-position" are applied
recursively to the parse tree of a function body.
* If a function call is in tail position, then it can be compiled using a
tail-call instruction.
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I compile
variable and argument references into slot indexes, which index into the
slot array of the current stack frame. So the scope of a variable name is
fixed at compile time, before the program is evaluated. This is probably
why the Curv interpreter is so much faster than the OpenSCAD interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD is
dynamically scoped. Variable and argument references are names, and these
names are looked up dynamically during evaluation, which leads to semantics
that are different from lexical scoping. The same variable name node in the
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and I'm
not sure there are any references to help you, because the dynamic scoping
semantics of OpenSCAD seem utterly unique to me.
On 31 January 2018 at 14:57, Torsten Paul <Torsten.Paul@gmx.de> wrote:
> The tail recursion optimization is only applied in two very specific
>> circumstances. It is not implemented in a general way.
>>
>> Do you have any pointers to documentation about how to implement tail
> recursion or how it's implemented in some language?
>
> I was searching for that some time ago and there's lots of discussion
> about how to use tail recursion but not so much about how to actually
> implement it.
>
> ciao,
> Torsten.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
SV
Simeon Veldstra
Thu, Feb 1, 2018 4:37 PM
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I compile
variable and argument references into slot indexes, which index into the
slot array of the current stack frame. So the scope of a variable name is
fixed at compile time, before the program is evaluated. This is probably
why the Curv interpreter is so much faster than the OpenSCAD interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD is
dynamically scoped. Variable and argument references are names, and these
names are looked up dynamically during evaluation, which leads to semantics
that are different from lexical scoping. The same variable name node in the
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and I'm
not sure there are any references to help you, because the dynamic scoping
semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
On 1/31/18, doug moen <doug@moens.org> wrote:
...
> One caveat. In the style of language interpreter I'm familiar with,
> variable and argument references are lexically scoped. In Curv, I compile
> variable and argument references into slot indexes, which index into the
> slot array of the current stack frame. So the scope of a variable name is
> fixed at compile time, before the program is evaluated. This is probably
> why the Curv interpreter is so much faster than the OpenSCAD interpreter:
> array indexing is faster than hash table lookup. By contrast, OpenSCAD is
> dynamically scoped. Variable and argument references are names, and these
> names are looked up dynamically during evaluation, which leads to semantics
> that are different from lexical scoping. The same variable name node in the
> parse tree may have different scopes in different calls to the same
> function or module. So you'll have to figure out how to preserve these
> dynamic scoping semantics if you implement this style of compiler, and I'm
> not sure there are any references to help you, because the dynamic scoping
> semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
DM
doug moen
Thu, Feb 1, 2018 5:13 PM
Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory of
Lisp interpreters helps much.
Example 1.
1 a = 0;
2 module m()
3 {
4 function f() = a;
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Look at line 4. In a lexically scoped language, the variable name 'a' would
statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
seems to be chosen at runtime, and varies dynamically. So the output is:
ECHO: 0,1
Example 2.
1 a = 0;
2 function f() = a;
3 module m()
4 {
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.
To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour of
existing programs.
On 1 February 2018 at 11:37, Simeon Veldstra sim@puddle.ca wrote:
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I compile
variable and argument references into slot indexes, which index into the
slot array of the current stack frame. So the scope of a variable name is
fixed at compile time, before the program is evaluated. This is probably
why the Curv interpreter is so much faster than the OpenSCAD interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD is
dynamically scoped. Variable and argument references are names, and these
names are looked up dynamically during evaluation, which leads to
that are different from lexical scoping. The same variable name node in
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and
not sure there are any references to help you, because the dynamic
semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_
Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory of
Lisp interpreters helps much.
Example 1.
1 a = 0;
2 module m()
3 {
4 function f() = a;
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Look at line 4. In a lexically scoped language, the variable name 'a' would
statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
seems to be chosen at runtime, and varies dynamically. So the output is:
ECHO: 0,1
Example 2.
1 a = 0;
2 function f() = a;
3 module m()
4 {
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.
To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour of
existing programs.
On 1 February 2018 at 11:37, Simeon Veldstra <sim@puddle.ca> wrote:
> On 1/31/18, doug moen <doug@moens.org> wrote:
> ...
> > One caveat. In the style of language interpreter I'm familiar with,
> > variable and argument references are lexically scoped. In Curv, I compile
> > variable and argument references into slot indexes, which index into the
> > slot array of the current stack frame. So the scope of a variable name is
> > fixed at compile time, before the program is evaluated. This is probably
> > why the Curv interpreter is so much faster than the OpenSCAD interpreter:
> > array indexing is faster than hash table lookup. By contrast, OpenSCAD is
> > dynamically scoped. Variable and argument references are names, and these
> > names are looked up dynamically during evaluation, which leads to
> semantics
> > that are different from lexical scoping. The same variable name node in
> the
> > parse tree may have different scopes in different calls to the same
> > function or module. So you'll have to figure out how to preserve these
> > dynamic scoping semantics if you implement this style of compiler, and
> I'm
> > not sure there are any references to help you, because the dynamic
> scoping
> > semantics of OpenSCAD seem utterly unique to me.
>
> Emacs lisp is another place where dynamic scope is found. This is
> considered a mistake by some, and lexical scoping has been added
> to recent versions.
> https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
>
> I suppose you could index into a global table of slots to
> implement dynamic scope in the compiler you describe, but it
> would not be an improvement, except perhaps if your goal was
> compatibility. Dynamically scoped programs are considerably
> harder to reason about than lexically scoped programs. This is
> true both for humans and optimizing compilers.
>
> You can get dynamic scope by accident in an interpreter with a
> slight change in how you handle the environment.
>
> The best explanation I've seen for this is published here:
> http://cs.brown.edu/courses/cs173/2012/book/From_
> Substitution_to_Environments.html
> In Shriram Krishnamurthi's excellent book
> Programming Languages: Application and Interpretation
>
>
> --
> sim
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
SV
Simeon Veldstra
Fri, Feb 2, 2018 3:21 AM
Oh, I see, "broken scoping" might be a clearer term perhaps.
The docs pretty clearly discourage reassignment at least, and the
language gives us let.
I was happy to discover OpenSCAD. Despite its warts, it suits my
style of thinking. ImplicitCAD and Ruckus were exciting to
find, but appear to be dead projects. I'm enjoying digging into
Curv, keep up the good work!
I'm quite sure you know more about language design than I do. I
find it a fascinating subject and thought my comments would be of
interest to the list.
On 2/1/18, doug moen doug@moens.org wrote:
Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory of
Lisp interpreters helps much.
Example 1.
1 a = 0;
2 module m()
3 {
4 function f() = a;
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Look at line 4. In a lexically scoped language, the variable name 'a' would
statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
seems to be chosen at runtime, and varies dynamically. So the output is:
ECHO: 0,1
Example 2.
1 a = 0;
2 function f() = a;
3 module m()
4 {
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.
To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour of
existing programs.
On 1 February 2018 at 11:37, Simeon Veldstra sim@puddle.ca wrote:
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I
compile
variable and argument references into slot indexes, which index into
the
slot array of the current stack frame. So the scope of a variable name
is
fixed at compile time, before the program is evaluated. This is
probably
why the Curv interpreter is so much faster than the OpenSCAD
interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD
is
dynamically scoped. Variable and argument references are names, and
these
names are looked up dynamically during evaluation, which leads to
that are different from lexical scoping. The same variable name node in
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and
not sure there are any references to help you, because the dynamic
semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_
Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Oh, I see, "broken scoping" might be a clearer term perhaps.
The docs pretty clearly discourage reassignment at least, and the
language gives us let.
I was happy to discover OpenSCAD. Despite its warts, it suits my
style of thinking. ImplicitCAD and Ruckus were exciting to
find, but appear to be dead projects. I'm enjoying digging into
Curv, keep up the good work!
I'm quite sure you know more about language design than I do. I
find it a fascinating subject and thought my comments would be of
interest to the list.
On 2/1/18, doug moen <doug@moens.org> wrote:
> Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
> dynamic scoping in Lisp. So I don't think that references to the theory of
> Lisp interpreters helps much.
>
> Example 1.
>
> 1 a = 0;
> 2 module m()
> 3 {
> 4 function f() = a;
> 5 x = f();
> 6 a = 1;
> 7 y = f();
> 8 echo(x,y);
> 9 }
> 10 m();
>
> Look at line 4. In a lexically scoped language, the variable name 'a' would
> statically refer to either the outer definition on line 1, or the inner
> definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
> seems to be chosen at runtime, and varies dynamically. So the output is:
> ECHO: 0,1
>
> Example 2.
>
> 1 a = 0;
> 2 function f() = a;
> 3 module m()
> 4 {
> 5 x = f();
> 6 a = 1;
> 7 y = f();
> 8 echo(x,y);
> 9 }
> 10 m();
>
> Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
> lexically scoped within the function 'f'. In the previous case, the
> variable 'a' was dynamically scoped within the function 'f'.
>
> To me, this looks like a bug. It's as if the intention was to implement
> lexical scoping, but the implementation is buggy. Marius has previously
> stated that we can't fix this bug in a way that changes the behaviour of
> existing programs.
>
> On 1 February 2018 at 11:37, Simeon Veldstra <sim@puddle.ca> wrote:
>
>> On 1/31/18, doug moen <doug@moens.org> wrote:
>> ...
>> > One caveat. In the style of language interpreter I'm familiar with,
>> > variable and argument references are lexically scoped. In Curv, I
>> > compile
>> > variable and argument references into slot indexes, which index into
>> > the
>> > slot array of the current stack frame. So the scope of a variable name
>> > is
>> > fixed at compile time, before the program is evaluated. This is
>> > probably
>> > why the Curv interpreter is so much faster than the OpenSCAD
>> > interpreter:
>> > array indexing is faster than hash table lookup. By contrast, OpenSCAD
>> > is
>> > dynamically scoped. Variable and argument references are names, and
>> > these
>> > names are looked up dynamically during evaluation, which leads to
>> semantics
>> > that are different from lexical scoping. The same variable name node in
>> the
>> > parse tree may have different scopes in different calls to the same
>> > function or module. So you'll have to figure out how to preserve these
>> > dynamic scoping semantics if you implement this style of compiler, and
>> I'm
>> > not sure there are any references to help you, because the dynamic
>> scoping
>> > semantics of OpenSCAD seem utterly unique to me.
>>
>> Emacs lisp is another place where dynamic scope is found. This is
>> considered a mistake by some, and lexical scoping has been added
>> to recent versions.
>> https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
>>
>> I suppose you could index into a global table of slots to
>> implement dynamic scope in the compiler you describe, but it
>> would not be an improvement, except perhaps if your goal was
>> compatibility. Dynamically scoped programs are considerably
>> harder to reason about than lexically scoped programs. This is
>> true both for humans and optimizing compilers.
>>
>> You can get dynamic scope by accident in an interpreter with a
>> slight change in how you handle the environment.
>>
>> The best explanation I've seen for this is published here:
>> http://cs.brown.edu/courses/cs173/2012/book/From_
>> Substitution_to_Environments.html
>> In Shriram Krishnamurthi's excellent book
>> Programming Languages: Application and Interpretation
>>
>>
>> --
>> sim
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
--
sim
T
TLC123
Fri, Feb 2, 2018 4:08 AM
only in the global scope.
a="GAH";
function x(b)=let(c=b) y();
function y()=c;
echo(x(a));
z();
module z(b){c=b; w();}
module w(){echo(c);}
--
Sent from: http://forum.openscad.org/
only in the global scope.
a="GAH";
function x(b)=let(c=b) y();
function y()=c;
echo(x(a));
z();
module z(b){c=b; w();}
module w(){echo(c);}
--
Sent from: http://forum.openscad.org/
DM
doug moen
Fri, Feb 2, 2018 4:19 AM
Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's
another implicit-function 3D modelling language, based on the Guile dialect
of Scheme. https://github.com/libfive/libfive
On 1 February 2018 at 22:21, Simeon Veldstra sim@puddle.ca wrote:
Oh, I see, "broken scoping" might be a clearer term perhaps.
The docs pretty clearly discourage reassignment at least, and the
language gives us let.
I was happy to discover OpenSCAD. Despite its warts, it suits my
style of thinking. ImplicitCAD and Ruckus were exciting to
find, but appear to be dead projects. I'm enjoying digging into
Curv, keep up the good work!
I'm quite sure you know more about language design than I do. I
find it a fascinating subject and thought my comments would be of
interest to the list.
On 2/1/18, doug moen doug@moens.org wrote:
Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory
Lisp interpreters helps much.
Example 1.
1 a = 0;
2 module m()
3 {
4 function f() = a;
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Look at line 4. In a lexically scoped language, the variable name 'a'
statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
seems to be chosen at runtime, and varies dynamically. So the output is:
ECHO: 0,1
Example 2.
1 a = 0;
2 function f() = a;
3 module m()
4 {
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.
To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour of
existing programs.
On 1 February 2018 at 11:37, Simeon Veldstra sim@puddle.ca wrote:
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I
compile
variable and argument references into slot indexes, which index into
the
slot array of the current stack frame. So the scope of a variable name
is
fixed at compile time, before the program is evaluated. This is
probably
why the Curv interpreter is so much faster than the OpenSCAD
interpreter:
array indexing is faster than hash table lookup. By contrast, OpenSCAD
is
dynamically scoped. Variable and argument references are names, and
these
names are looked up dynamically during evaluation, which leads to
that are different from lexical scoping. The same variable name node
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve these
dynamic scoping semantics if you implement this style of compiler, and
not sure there are any references to help you, because the dynamic
semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_
Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's
another implicit-function 3D modelling language, based on the Guile dialect
of Scheme. https://github.com/libfive/libfive
On 1 February 2018 at 22:21, Simeon Veldstra <sim@puddle.ca> wrote:
> Oh, I see, "broken scoping" might be a clearer term perhaps.
>
> The docs pretty clearly discourage reassignment at least, and the
> language gives us let.
>
> I was happy to discover OpenSCAD. Despite its warts, it suits my
> style of thinking. ImplicitCAD and Ruckus were exciting to
> find, but appear to be dead projects. I'm enjoying digging into
> Curv, keep up the good work!
>
> I'm quite sure you know more about language design than I do. I
> find it a fascinating subject and thought my comments would be of
> interest to the list.
>
> On 2/1/18, doug moen <doug@moens.org> wrote:
> > Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
> > dynamic scoping in Lisp. So I don't think that references to the theory
> of
> > Lisp interpreters helps much.
> >
> > Example 1.
> >
> > 1 a = 0;
> > 2 module m()
> > 3 {
> > 4 function f() = a;
> > 5 x = f();
> > 6 a = 1;
> > 7 y = f();
> > 8 echo(x,y);
> > 9 }
> > 10 m();
> >
> > Look at line 4. In a lexically scoped language, the variable name 'a'
> would
> > statically refer to either the outer definition on line 1, or the inner
> > definition on line 6. One or the other. But in OpenSCAD the scope of 'a'
> > seems to be chosen at runtime, and varies dynamically. So the output is:
> > ECHO: 0,1
> >
> > Example 2.
> >
> > 1 a = 0;
> > 2 function f() = a;
> > 3 module m()
> > 4 {
> > 5 x = f();
> > 6 a = 1;
> > 7 y = f();
> > 8 echo(x,y);
> > 9 }
> > 10 m();
> >
> > Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
> > lexically scoped within the function 'f'. In the previous case, the
> > variable 'a' was dynamically scoped within the function 'f'.
> >
> > To me, this looks like a bug. It's as if the intention was to implement
> > lexical scoping, but the implementation is buggy. Marius has previously
> > stated that we can't fix this bug in a way that changes the behaviour of
> > existing programs.
> >
> > On 1 February 2018 at 11:37, Simeon Veldstra <sim@puddle.ca> wrote:
> >
> >> On 1/31/18, doug moen <doug@moens.org> wrote:
> >> ...
> >> > One caveat. In the style of language interpreter I'm familiar with,
> >> > variable and argument references are lexically scoped. In Curv, I
> >> > compile
> >> > variable and argument references into slot indexes, which index into
> >> > the
> >> > slot array of the current stack frame. So the scope of a variable name
> >> > is
> >> > fixed at compile time, before the program is evaluated. This is
> >> > probably
> >> > why the Curv interpreter is so much faster than the OpenSCAD
> >> > interpreter:
> >> > array indexing is faster than hash table lookup. By contrast, OpenSCAD
> >> > is
> >> > dynamically scoped. Variable and argument references are names, and
> >> > these
> >> > names are looked up dynamically during evaluation, which leads to
> >> semantics
> >> > that are different from lexical scoping. The same variable name node
> in
> >> the
> >> > parse tree may have different scopes in different calls to the same
> >> > function or module. So you'll have to figure out how to preserve these
> >> > dynamic scoping semantics if you implement this style of compiler, and
> >> I'm
> >> > not sure there are any references to help you, because the dynamic
> >> scoping
> >> > semantics of OpenSCAD seem utterly unique to me.
> >>
> >> Emacs lisp is another place where dynamic scope is found. This is
> >> considered a mistake by some, and lexical scoping has been added
> >> to recent versions.
> >> https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
> >>
> >> I suppose you could index into a global table of slots to
> >> implement dynamic scope in the compiler you describe, but it
> >> would not be an improvement, except perhaps if your goal was
> >> compatibility. Dynamically scoped programs are considerably
> >> harder to reason about than lexically scoped programs. This is
> >> true both for humans and optimizing compilers.
> >>
> >> You can get dynamic scope by accident in an interpreter with a
> >> slight change in how you handle the environment.
> >>
> >> The best explanation I've seen for this is published here:
> >> http://cs.brown.edu/courses/cs173/2012/book/From_
> >> Substitution_to_Environments.html
> >> In Shriram Krishnamurthi's excellent book
> >> Programming Languages: Application and Interpretation
> >>
> >>
> >> --
> >> sim
> >>
> >> _______________________________________________
> >> OpenSCAD mailing list
> >> Discuss@lists.openscad.org
> >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
> >>
> >
>
>
> --
> sim
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
DM
doug moen
Fri, Feb 2, 2018 4:24 AM
Torleif: With macOS version 2017.12.23, I get
WARNING: Ignoring unknown variable 'c'.
ECHO: undef
WARNING: Ignoring unknown variable 'c'.
ECHO: undef
On 1 February 2018 at 23:08, TLC123 torleif.ceder@gmail.com wrote:
Torleif: With macOS version 2017.12.23, I get
WARNING: Ignoring unknown variable 'c'.
ECHO: undef
WARNING: Ignoring unknown variable 'c'.
ECHO: undef
On 1 February 2018 at 23:08, TLC123 <torleif.ceder@gmail.com> wrote:
> only in the global scope.
>
>
> a="GAH";
>
> function x(b)=let(c=b) y();
> function y()=c;
>
> echo(x(a));
> z();
> module z(b){c=b; w();}
> module w(){echo(c);}
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
T
TLC123
Fri, Feb 2, 2018 4:43 AM
So do I on win7. This to me indicates that scope is not nesting besides the
global.
I never had any issues with this as i tend to pass parameters with the
function and i don't try to tricky around no reassign paradigm.
Obviously it would be handy to not pass huge lists down a recursion.
But once again let's remind. Openscad script is not programming it is cad
markup. There are no variables. Only constants defined with evaluated
expressions.
--
Sent from: http://forum.openscad.org/
So do I on win7. This to me indicates that scope is not nesting besides the
global.
I never had any issues with this as i tend to pass parameters with the
function and i don't try to tricky around no reassign paradigm.
Obviously it would be handy to not pass huge lists down a recursion.
But once again let's remind. Openscad script is not programming it is cad
markup. There are no variables. Only constants defined with evaluated
expressions.
--
Sent from: http://forum.openscad.org/
T
TLC123
Fri, Feb 2, 2018 4:45 AM
So do I on win7. This to me indicates that scope is not nesting besides the
global. I never had any issues with this as i tend to pass parameters with
the function and i don't try to tricky around no reassign paradigm.Obviously
it would be handy to not pass huge lists down a recursion. But once again
let's remind. Openscad script is not programming it is cad markup. There
are no variables. Only constants defined with evaluated expressions.
--
Sent from: http://forum.openscad.org/
So do I on win7. This to me indicates that scope is not nesting besides the
global. I never had any issues with this as i tend to pass parameters with
the function and i don't try to tricky around no reassign paradigm.Obviously
it would be handy to not pass huge lists down a recursion. But once again
let's remind. Openscad script is not programming it is cad markup. There
are no variables. Only constants defined with evaluated expressions.
--
Sent from: http://forum.openscad.org/
DM
doug moen
Fri, Feb 2, 2018 1:16 PM
Torleif: If I add 'c="TOP";' to the beginning of the program, then I get
ECHO: "TOP"
ECHO: "TOP"
which is correct behaviour for lexical scoping.
If I then wrap this entire program in a module, like so:
module top()
{
...
}
top();
then the behaviour doesn't change, so that's lexical scoping inside a
module.
On 1 February 2018 at 23:45, TLC123 torleif.ceder@gmail.com wrote:
So do I on win7. This to me indicates that scope is not nesting besides
the global. I never had any issues with this as i tend to pass parameters
with the function and i don't try to tricky around no reassign paradigm.
Obviously it would be handy to not pass huge lists down a recursion. But
once again let's remind. Openscad script is not programming it is cad
markup. There are no variables. Only constants defined with evaluated
expressions.
Sent from the OpenSCAD mailing list archive http://forum.openscad.org/
at Nabble.com.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Torleif: If I add 'c="TOP";' to the beginning of the program, then I get
ECHO: "TOP"
ECHO: "TOP"
which is correct behaviour for lexical scoping.
If I then wrap this entire program in a module, like so:
module top()
{
...
}
top();
then the behaviour doesn't change, so that's lexical scoping inside a
module.
On 1 February 2018 at 23:45, TLC123 <torleif.ceder@gmail.com> wrote:
> So do I on win7. This to me indicates that scope is not nesting besides
> the global. I never had any issues with this as i tend to pass parameters
> with the function and i don't try to tricky around no reassign paradigm.
> Obviously it would be handy to not pass huge lists down a recursion. But
> once again let's remind. Openscad script is not programming it is cad
> markup. There are no variables. Only constants defined with evaluated
> expressions.
> ------------------------------
> Sent from the OpenSCAD mailing list archive <http://forum.openscad.org/>
> at Nabble.com.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
SV
Simeon Veldstra
Sat, Feb 3, 2018 6:36 PM
Oh wow...
Thanks for the tip!
On 2/1/18, doug moen doug@moens.org wrote:
Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's
another implicit-function 3D modelling language, based on the Guile dialect
of Scheme. https://github.com/libfive/libfive
On 1 February 2018 at 22:21, Simeon Veldstra sim@puddle.ca wrote:
Oh, I see, "broken scoping" might be a clearer term perhaps.
The docs pretty clearly discourage reassignment at least, and the
language gives us let.
I was happy to discover OpenSCAD. Despite its warts, it suits my
style of thinking. ImplicitCAD and Ruckus were exciting to
find, but appear to be dead projects. I'm enjoying digging into
Curv, keep up the good work!
I'm quite sure you know more about language design than I do. I
find it a fascinating subject and thought my comments would be of
interest to the list.
On 2/1/18, doug moen doug@moens.org wrote:
Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory
Lisp interpreters helps much.
Example 1.
1 a = 0;
2 module m()
3 {
4 function f() = a;
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Look at line 4. In a lexically scoped language, the variable name 'a'
statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of
'a'
seems to be chosen at runtime, and varies dynamically. So the output
is:
ECHO: 0,1
Example 2.
1 a = 0;
2 function f() = a;
3 module m()
4 {
5 x = f();
6 a = 1;
7 y = f();
8 echo(x,y);
9 }
10 m();
Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.
To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour
of
existing programs.
On 1 February 2018 at 11:37, Simeon Veldstra sim@puddle.ca wrote:
One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I
compile
variable and argument references into slot indexes, which index into
the
slot array of the current stack frame. So the scope of a variable
name
is
fixed at compile time, before the program is evaluated. This is
probably
why the Curv interpreter is so much faster than the OpenSCAD
interpreter:
array indexing is faster than hash table lookup. By contrast,
OpenSCAD
is
dynamically scoped. Variable and argument references are names, and
these
names are looked up dynamically during evaluation, which leads to
that are different from lexical scoping. The same variable name node
parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve
these
dynamic scoping semantics if you implement this style of compiler,
and
not sure there are any references to help you, because the dynamic
semantics of OpenSCAD seem utterly unique to me.
Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.
You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.
The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_
Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation
--
sim
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Oh wow...
Thanks for the tip!
On 2/1/18, doug moen <doug@moens.org> wrote:
> Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's
> another implicit-function 3D modelling language, based on the Guile dialect
> of Scheme. https://github.com/libfive/libfive
>
> On 1 February 2018 at 22:21, Simeon Veldstra <sim@puddle.ca> wrote:
>
>> Oh, I see, "broken scoping" might be a clearer term perhaps.
>>
>> The docs pretty clearly discourage reassignment at least, and the
>> language gives us let.
>>
>> I was happy to discover OpenSCAD. Despite its warts, it suits my
>> style of thinking. ImplicitCAD and Ruckus were exciting to
>> find, but appear to be dead projects. I'm enjoying digging into
>> Curv, keep up the good work!
>>
>> I'm quite sure you know more about language design than I do. I
>> find it a fascinating subject and thought my comments would be of
>> interest to the list.
>>
>> On 2/1/18, doug moen <doug@moens.org> wrote:
>> > Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
>> > dynamic scoping in Lisp. So I don't think that references to the theory
>> of
>> > Lisp interpreters helps much.
>> >
>> > Example 1.
>> >
>> > 1 a = 0;
>> > 2 module m()
>> > 3 {
>> > 4 function f() = a;
>> > 5 x = f();
>> > 6 a = 1;
>> > 7 y = f();
>> > 8 echo(x,y);
>> > 9 }
>> > 10 m();
>> >
>> > Look at line 4. In a lexically scoped language, the variable name 'a'
>> would
>> > statically refer to either the outer definition on line 1, or the inner
>> > definition on line 6. One or the other. But in OpenSCAD the scope of
>> > 'a'
>> > seems to be chosen at runtime, and varies dynamically. So the output
>> > is:
>> > ECHO: 0,1
>> >
>> > Example 2.
>> >
>> > 1 a = 0;
>> > 2 function f() = a;
>> > 3 module m()
>> > 4 {
>> > 5 x = f();
>> > 6 a = 1;
>> > 7 y = f();
>> > 8 echo(x,y);
>> > 9 }
>> > 10 m();
>> >
>> > Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
>> > lexically scoped within the function 'f'. In the previous case, the
>> > variable 'a' was dynamically scoped within the function 'f'.
>> >
>> > To me, this looks like a bug. It's as if the intention was to implement
>> > lexical scoping, but the implementation is buggy. Marius has previously
>> > stated that we can't fix this bug in a way that changes the behaviour
>> > of
>> > existing programs.
>> >
>> > On 1 February 2018 at 11:37, Simeon Veldstra <sim@puddle.ca> wrote:
>> >
>> >> On 1/31/18, doug moen <doug@moens.org> wrote:
>> >> ...
>> >> > One caveat. In the style of language interpreter I'm familiar with,
>> >> > variable and argument references are lexically scoped. In Curv, I
>> >> > compile
>> >> > variable and argument references into slot indexes, which index into
>> >> > the
>> >> > slot array of the current stack frame. So the scope of a variable
>> >> > name
>> >> > is
>> >> > fixed at compile time, before the program is evaluated. This is
>> >> > probably
>> >> > why the Curv interpreter is so much faster than the OpenSCAD
>> >> > interpreter:
>> >> > array indexing is faster than hash table lookup. By contrast,
>> >> > OpenSCAD
>> >> > is
>> >> > dynamically scoped. Variable and argument references are names, and
>> >> > these
>> >> > names are looked up dynamically during evaluation, which leads to
>> >> semantics
>> >> > that are different from lexical scoping. The same variable name node
>> in
>> >> the
>> >> > parse tree may have different scopes in different calls to the same
>> >> > function or module. So you'll have to figure out how to preserve
>> >> > these
>> >> > dynamic scoping semantics if you implement this style of compiler,
>> >> > and
>> >> I'm
>> >> > not sure there are any references to help you, because the dynamic
>> >> scoping
>> >> > semantics of OpenSCAD seem utterly unique to me.
>> >>
>> >> Emacs lisp is another place where dynamic scope is found. This is
>> >> considered a mistake by some, and lexical scoping has been added
>> >> to recent versions.
>> >> https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding
>> >>
>> >> I suppose you could index into a global table of slots to
>> >> implement dynamic scope in the compiler you describe, but it
>> >> would not be an improvement, except perhaps if your goal was
>> >> compatibility. Dynamically scoped programs are considerably
>> >> harder to reason about than lexically scoped programs. This is
>> >> true both for humans and optimizing compilers.
>> >>
>> >> You can get dynamic scope by accident in an interpreter with a
>> >> slight change in how you handle the environment.
>> >>
>> >> The best explanation I've seen for this is published here:
>> >> http://cs.brown.edu/courses/cs173/2012/book/From_
>> >> Substitution_to_Environments.html
>> >> In Shriram Krishnamurthi's excellent book
>> >> Programming Languages: Application and Interpretation
>> >>
>> >>
>> >> --
>> >> sim
>> >>
>> >> _______________________________________________
>> >> OpenSCAD mailing list
>> >> Discuss@lists.openscad.org
>> >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>> >>
>> >
>>
>>
>> --
>> sim
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
--
sim
R
runsun
Sat, Feb 10, 2018 1:02 AM
What is the application of a 2-3 tree in geometry, specifically in OpenSCAD?
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
What is the application of a 2-3 tree in geometry, specifically in OpenSCAD?
-----
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
N
NateTG
Sat, Feb 10, 2018 4:01 AM
What is the application of a 2-3 tree in geometry, specifically in
OpenSCAD?
I did it as an exercise. I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge lists
instead.
--
Sent from: http://forum.openscad.org/
runsun wrote
> What is the application of a 2-3 tree in geometry, specifically in
> OpenSCAD?
I did it as an exercise. I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge lists
instead.
--
Sent from: http://forum.openscad.org/
R
runsun
Mon, Feb 12, 2018 10:53 PM
I did it as an exercise. I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge
lists
instead.
Thx Nate. I might find it useful later when doing some geometrical calc.
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
NateTG wrote
> I did it as an exercise. I expected there to be a need for a data
> structure with search,insert and remove, but I guess people just munge
> lists
> instead.
Thx Nate. I might find it useful later when doing some geometrical calc.
-----
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Mon, Feb 12, 2018 11:49 PM
A 2-3 tree is an efficient balanced tree and it might be helpful to store
geometrical data that should be sorted for fast retrieval. To be useful, a
2-3 tree data structure should store a register containing the relevant
geometrical data besides the sorting key. A simple example of that is the
quicksort method that is found in the OpenSCAD manual [1]. It allows to
sort an array of numbers. However, it is useless to sort a list of points
by its first coordinates a much more useful task. I had to write my own
version.
[1]
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_Comprehensions#Sorting_a_vector
2018-02-12 20:53 GMT-02:00 runsun runsun@gmail.com:
I did it as an exercise. I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge
lists
instead.
Thx Nate. I might find it useful later when doing some geometrical calc.
A 2-3 tree is an efficient balanced tree and it might be helpful to store
geometrical data that should be sorted for fast retrieval. To be useful, a
2-3 tree data structure should store a register containing the relevant
geometrical data besides the sorting key. A simple example of that is the
quicksort method that is found in the OpenSCAD manual [1]. It allows to
sort an array of numbers. However, it is useless to sort a list of points
by its first coordinates a much more useful task. I had to write my own
version.
[1]
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_Comprehensions#Sorting_a_vector
2018-02-12 20:53 GMT-02:00 runsun <runsun@gmail.com>:
> NateTG wrote
> > I did it as an exercise. I expected there to be a need for a data
> > structure with search,insert and remove, but I guess people just munge
> > lists
> > instead.
>
> Thx Nate. I might find it useful later when doing some geometrical calc.
>
N
NateTG
Tue, Feb 13, 2018 1:30 AM
Yeah, it would be nice if there were a way to make the tree generic so that
it works with many different comparison functions.
--
Sent from: http://forum.openscad.org/
Yeah, it would be nice if there were a way to make the tree generic so that
it works with many different comparison functions.
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Tue, Feb 13, 2018 2:30 AM
Yeah, it would be nice if there were a way to make the tree generic so that
it works with many different comparison functions.
Since we don't have first class functions in OpenSCAD, a simple alternative
solution would be to insert arrays in the tree: by convention the sorting
key is the first element of the array and the remaining is something to be
interpreted by the calling code. Like, for instance:
function quick_sort(arr, simple=true) =
!(len(arr)>0) ? [] :
let( pivot = simple ? arr[floor(len(arr)/2)] :
arr[floor(len(arr)/2)][0],
lesser = [ for (y = arr) if ( (simple ? y : y[0]) < pivot ) y ],
equal = [ for (y = arr) if ( (simple ? y : y[0]) == pivot ) y ],
greater = [ for (y = arr) if ( (simple ? y : y[0]) > pivot ) y ]
)
concat( quick_sort(lesser, simple),
equal,
quick_sort(greater, simple) );
l = [ 5,2,8,0,4,7 ]; // simple list of numbers
echo(quick_sort(l));
// ECHO: [0, 2, 4, 5, 7, 8]
v = [ [-1,2], [-7,[4]], [-9,2,4,5], [-3] ]; // list of non void lists
echo(quick_sort(v, simple=false));
// ECHO: [[-9, 2, 4, 5], [-7, [4]], [-3], [-1, 2]]
> Yeah, it would be nice if there were a way to make the tree generic so that
> it works with many different comparison functions.
Since we don't have first class functions in OpenSCAD, a simple alternative
solution would be to insert arrays in the tree: by convention the sorting
key is the first element of the array and the remaining is something to be
interpreted by the calling code. Like, for instance:
function quick_sort(arr, simple=true) =
!(len(arr)>0) ? [] :
let( pivot = simple ? arr[floor(len(arr)/2)] :
arr[floor(len(arr)/2)][0],
lesser = [ for (y = arr) if ( (simple ? y : y[0]) < pivot ) y ],
equal = [ for (y = arr) if ( (simple ? y : y[0]) == pivot ) y ],
greater = [ for (y = arr) if ( (simple ? y : y[0]) > pivot ) y ]
)
concat( quick_sort(lesser, simple),
equal,
quick_sort(greater, simple) );
l = [ 5,2,8,0,4,7 ]; // simple list of numbers
echo(quick_sort(l));
// ECHO: [0, 2, 4, 5, 7, 8]
v = [ [-1,2], [-7,[4]], [-9,2,4,5], [-3] ]; // list of non void lists
echo(quick_sort(v, simple=false));
// ECHO: [[-9, 2, 4, 5], [-7, [4]], [-3], [-1, 2]]
R
runsun
Tue, Feb 13, 2018 5:50 PM
@Ronaldo, nice code. It's very fast -- sorting 100,000 pts 10 times cost only
6 sec. Pulling the 'simple?' check out of the loops, like this:
<blockquote>
function quick_sort_1(arr, simple=true) =
(
!(len(arr)>0) ? [] :
let( pivot = simple ? arr[floor(len(arr)/2)] :
arr[floor(len(arr)/2)][0],
lesser = simple? [ for (y = arr) if ( y < pivot ) y ]
: [ for (y = arr) if ( y[0] < pivot ) y ],
,
equal = simple? [ for (y = arr) if ( y == pivot ) y ]
: [ for (y = arr) if ( y[0] == pivot ) y ] ,
greater = simple? [ for (y = arr) if ( y > pivot ) y ]
: [ for (y = arr) if ( y[0] > pivot ) y ]
)
concat( quick_sort_1(lesser, simple),
equal,
quick_sort_1(greater, simple) )
);
</blockquote>
gained a little speed (100,000 x 10 => 5 sec) but i guess even in extreme
use of OpenSCAD it doesn't matter.
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
@Ronaldo, nice code. It's very fast -- sorting 100,000 pts 10 times cost only
6 sec. Pulling the 'simple?' check out of the loops, like this:
<blockquote>
function quick_sort_1(arr, simple=true) =
(
!(len(arr)>0) ? [] :
let( pivot = simple ? arr[floor(len(arr)/2)] :
arr[floor(len(arr)/2)][0],
lesser = simple? [ for (y = arr) if ( y < pivot ) y ]
: [ for (y = arr) if ( y[0] < pivot ) y ],
,
equal = simple? [ for (y = arr) if ( y == pivot ) y ]
: [ for (y = arr) if ( y[0] == pivot ) y ] ,
greater = simple? [ for (y = arr) if ( y > pivot ) y ]
: [ for (y = arr) if ( y[0] > pivot ) y ]
)
concat( quick_sort_1(lesser, simple),
equal,
quick_sort_1(greater, simple) )
);
</blockquote>
gained a little speed (100,000 x 10 => 5 sec) but i guess even in extreme
use of OpenSCAD it doesn't matter.
-----
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
R
runsun
Tue, Feb 13, 2018 6:13 PM
Inspired by Ronaldo's code, here is a more generalized quick sort for arrays:
<br/>
<blockquote>
function sortArrs(arrs, by=0)=
(
!(len(arrs)>0) ? [] :
let( pivot = arrs[floor(len(arrs)/2)][by],
lesser = [ for (y = arrs) if ( y[by] < pivot ) y ],
,
equal = [ for (y = arrs) if ( y[by] == pivot ) y ] ,
greater = [ for (y = arrs) if ( y[by] > pivot ) y ]
)
concat( sortArrs(lesser, by),
equal,
sortArrs(greater, by)
)
);</blockquote>
/*
ECHO: [[2.9, 1.9, -2.8], [-2.5, -1.5, -2.9], [-0.1, 2.8, 2.4], [0.9, 1.8,
0.8], [-1, -1.3, -0.8]]
[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [-0.1, 2.8, 2.4]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]]
[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]
, [-0.1, 2.8, 2.4]
]
[[-2.5, -1.5, -2.9]
, [2.9, 1.9, -2.8]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [-0.1, 2.8, 2.4]]
*/
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/
Inspired by Ronaldo's code, here is a more generalized quick sort for arrays:
<br/>
<blockquote>
function sortArrs(arrs, by=0)=
(
!(len(arrs)>0) ? [] :
let( pivot = arrs[floor(len(arrs)/2)][by],
lesser = [ for (y = arrs) if ( y[by] < pivot ) y ],
,
equal = [ for (y = arrs) if ( y[by] == pivot ) y ] ,
greater = [ for (y = arrs) if ( y[by] > pivot ) y ]
)
concat( sortArrs(lesser, by),
equal,
sortArrs(greater, by)
)
);</blockquote>
/*
ECHO: [[2.9, 1.9, -2.8], [-2.5, -1.5, -2.9], [-0.1, 2.8, 2.4], [0.9, 1.8,
0.8], [-1, -1.3, -0.8]]
[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [-0.1, 2.8, 2.4]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]]
[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]
, [-0.1, 2.8, 2.4]
]
[[-2.5, -1.5, -2.9]
, [2.9, 1.9, -2.8]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [-0.1, 2.8, 2.4]]
*/
-----
$ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); $ tips: Collection of tips on github
--
Sent from: http://forum.openscad.org/