Comments on: TAKE Action
A lot of language design is concern over little things - many little things.
There is a functional asymmetry in REBOL's series access model. It pops up when you write objects like I/O ports (which I tend to be doing a lot these days.) A voice has been telling me to fix this for a long time.
(Is 8 years a long time?)
Those of you who use queues or stacks may already know what I'm talking about. Implement a queue (FIFO):
queue: make block! 20
; To enqueue (add new item):
append queue item
; To dequeue (get top item):
item: first queue
(Note that in R3, first on empty queue is not an error, so the above works.)
The issue: that last section cannot be written as a single sentence. The function stream is interrupted, which becomes obvious in this case:
dequeue: func [queue /local item][
item: first queue
So, enqueuing is a singular series action, but dequeue cannot be.
This violates the "simple as possible" design rule of REBOL... because there is indeed a simple alternative.
The solution is:
; To dequeue (get top item):
item: take queue
Here, take is equivalent to a first and remove on the series. The dequeue function written above becomes trivial, so you no longer need it.
What if you want to take more than just one?
items: take/part queue 3
and now items is a series itself.
And finally, what about stacks?
We know that implementing a stack with top-at-tail gets us the best performance. So, we would want:
item: take/last queue
which roughly does a:
item: take back tail queue
We can also allow:
items: take/last/part queue -3
The reason for the negative number comes from this property:
items: take/part at queue 5 -3
So, take works anywhere within the series.
I like the functionality. Might common stack functions names (e.g., pop, push, top, etc.) be more appropriate here?|
Edoc, I don't want push, pop, etc. as standard; you can alias them easily enough if you want. They're just more words to remember, and since a series is just a series, they would have to always operate the same; i.e. you couldn't use them for both stacks and queues.
I used to look for very specific, unique, words for methodds in each class I wrote, so stacks and queues had those specific names. After I read Bertand Meyer's 'Reusable Software', where he talks about the advantage of a Linnaean naming convention, and after finding REBOL, which uses it so well, I greatly prefer it for general use.
I don't get the need for a negative range val with /last though. I can see it working with AT, but if /last means "from the tail", then the take can only go one direction, right?
I know negative values are often used to indicate the reverse of a positive arg, but it doesn't make things clearer most of the time; it's just something to learn to watch out for.
I mention it because pop and top are already quite familiar to most programmers as list/set/stack functions and the meaning might be more straight-forward than 'take. The first time someone encounters 'take, they will definitely be headed for the REBOL function dictionary. It's no big deal to me, but 'take doesn't suggest to me that it's a destructive operation and not a copy. Just my $.02, no gripes.
Edoc, here you go:
push: func [s x] [append/only :s :x]
pop: func [s] [take/last :s -1]
Greg, I'm not sure I agree with you on the negative lengths. It is too valuable to be able to remove values from before the position when you are in the middle of the series. If you wanted to get rid of negative length arguments you would need to add a /BACK refinement to get the same effect, and then just say that /LAST implies /BACK. I'm not sure that I would give up the potential benefit of arithmetic on the length argument...
I'm curious how TAKE would behave in certain cases.
Overall, TAKE sounds like a great addition to the toolkit.
- Can you return the value wrapped in a series if you TAKE/PART s 1, just like you do when you TAKE/PART s 2, for consistency? Can TAKE/PART s 0 return an empty series of the type of s, also for consistency? That would make arithmetic on the length more useful.
- It would be nice if TAKE would return NONE if there was nothing to take, like the new behavior of FIRST.
- What does TAKE/PART return when there aren't enough values? I would suggest a series of the length that it can get, of the values that are there. If there are no values return an empty series as above. With the changes to FIRST and such it would be the same as NONE padding, but more useful since you would get the available length too.
I'm not grumpy-- the name 'take got stuck in my craw, that's all. Maybe I'm alone on this one. Seems like there should be plenty of other candidates which describe the action more naturally. E.g., 'clip, 'cue, 'snip, etc.
Ok, I've had my say. :^)
Edoc: if you look up the meaning of 'TAKE, its quite exact in this useage, take means remove from someone else which is exactly what carl decribed.
also, as noted, take can retrieve from the middle too, so the other words would become ambiguous.
No problem, I'm over it.|
I think TAKE was the word that came out on top in the previous, long, discussion on this function. CUT and PULL also came up.
"I'm not sure I agree with you on the negative lengths. It is too valuable to be able to remove values from before the position when you are in the middle of the series."
I agree 100% Brian, my only issue is with requiring them when used with /LAST.
I think the behavior of /PART should match COPY/PART, while a plain TAKE is like PICK. Something like this maybe (UNTESTED! OK, I ran a couple quick tests):
take: func [
"Remove and return items from a series."
series [series! none!]
/last "Take from the tail, not the head"
/part "The number of items to pull; one is the default"
if none? series [return none]
range: any [range 1]
either last [
; force negative range if taking from tail.
series: skip tail series negate abs range
result: either part [copy series] [system/words/last series]
either part [
result: copy/part series range
remove/part series range
result: first series
Maybe a good name or alias for 'take/last would be 'curtail
curtail: "to cut short; cut off a part of; abridge; reduce; diminish."
Curtail is not a good word for this func IMO. For one thing, it doesn't mean the right thing if you're taking an item from anywhere other than the tail of the series.|
Reading closely, I think we both agree.
Post a Comment:
You can post a comment here. Keep it on-topic.