-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Samefringe #345
Comments
Coming back to this musing much later, I now think expressing the solution (slightly longer) as a That is, instead of this code:
We'd write this code:
Where I like to use All of this was triggered by reading the code at the top of page 268 of this paper. It just occurred to me how nice it would be for a language to be general enough to just be able to plug its |
Although I do seem to be stuck in an annoying oscillation between explicit step-by-step thinking and descriptive/declarative verbs — when I look at that |
Bob Nystrom has a blog post called Iteration Inside and Out where he talks about "interleaving two sequences" (using internal iterators, described in the blog post):
Change the final "continuations" to "coroutines", and I agree; that's what makes the above solutions nice. The paper On the Dynamic Extent of Delimited Continuations uses samefringe as a motivating example to explore different strategies (some of them using delimited continuations) for traversal. |
Hello past me, this is future me. I can imagine a shorter, more elegant solution:
Where Yes, there is a "playing with fire" element in there, in that The |
Let's define a (binary)
Tree
as being either aNode
or aLeaf
. In current 007, we can create and inspect trees like this:In Awesome Future 007, I'd like to be able to write it like this:
But never mind, the former version works today, so let's go with that.
Now we can form trees that are distinct but have the same fringe, that is, the same preorder traversal of leaf nodes:
Computing the actual fringe of a tree, in order to compare it against another fringe, is straightforward.
The two trees are shown to have the same fringe.
Whereas a tree having exactly the same shape as
tree2
but a different value in one of the leaves does not have the same fringe.And we're done.
Except we're not really done, because (quoting Rosetta Code)
The challenge, if you ask me, is to both get the above minimum-work elegance, while also retaining a simple, understandable structure in the program.
Here's how I would like to write the solution, again in Wishful Thinking 007:
I cannot imagine a shorter, more elegant solution to the problem than the above, not without starting to drop detail. But there are a number of assumptions here:
all()
would take something iterable and returns the first falsy value if any, orTrue
.fringe(node)
cannot be the eager variant implemented above. Instead it needs to be a coroutine:(I've borrowed JavaScript's syntax with the star. Python manages to do this without the star. Similarly, the
yield*
keyword, also from JavaScript, is short forfor <expr> -> v { yield v; }
.)Finally, the existence of the zip metaoperator is assumed here. This operator is also lazy, making the whole
sameFringe
function evaluate only the minimum necessary. (I originally wrote it as»==«
, but the hyperoperator is not lazy.)One difference to Perl 6's(Much later edit: although, thinking about it, the "stop at shortest" strategy is the natural one. Perhaps better to extend the two sequences with a freshly-unique stopper?)Z==
is that we don't want to truncate on the length of the shortest operand; instead if one is shorter than the other, we want to returnFalse
. A hybrid betweenZ==
and Python'szip_longest
would be ideal here.I'm not super-sure what's the conclusion here. I think we should add all that laziness and iterables into 007, since we're heading there anyway. Long-term, I think we should have coroutines/
yield
, also, mostly because since we already haveupwards funargsfirst-class closures, the mechanism for having coroutines is mostly there already.I'm curious what the limits are to statically optimizing a line of code such as
all(fringe(tree1) Z== fringe(tree2))
. Ideally I would have it set up two generators, pull them in a while loop, and returnFalse
as soon as it found a discrepancy. What, pray tell, would prevent the inlining ofZ==
andall()
to make that happen?Going to close this issue immediately though, because it was more of an exploration of ideas anyway. Might be good to have it linkable in the future.
Here, as a parting gift, I was able to put together a lazy solution using Today Working 007, by porting a Scheme implementation using continuation-passing style that I found on Ward's Wiki:
lazyFlatten
is kinda similar to a coroutinefringe
, andstreamEqual
is basicallyall
andZ==
in spirit. The callbacks andtuplesarrays constitute the inelegance in the shape of poor man's coroutines.But it works!
Further reading:
SAMEFRINGE
on page 9The text was updated successfully, but these errors were encountered: