Queues do not give control over that structure though. Fred wrote a good article on zippers: http://ferd.ca/yet-another-article-on-zippers.html
On Monday, August 29, 2016, Ben Wilson <benwilson...@gmail.com> wrote: > Sounds at least a bit like http://erlang.org/doc/man/queue.html > > On Monday, August 29, 2016 at 6:57:08 PM UTC-4, OvermindDL1 wrote: >> >> Entirely different concepts. :-) >> >> 'zipping' a list means combining multiple lists into a single list of >> tuple/lists, which is what Enum/Stream does. >> >> A Zipper data structure is essentially a tuple of two lists, for example: >> {[], [1, 2, 3, 4]} >> -> Move right >> -> {[1], [2, 3, 4]} >> -> Move right >> -> {[2, 1], [3, 4]} >> -> Move right >> -> {[3, 2, 1], [4]} >> -> Move left >> -> {[2, 1], [3, 4]} >> >> It is basically a way of iterating backwards and forward through a list >> (which is what a BF interpreter does to access its memory) in a very >> efficient, functional, immutable method. The left side holds the reversed >> 'data' to the left of your 'cursor', and the right holds the 'data' to the >> right of your 'cursor', where the cursor is just the current position along >> the entire structure. >> >> Zippers can also be more complicated as well, allowing you to efficiently >> and immutable walk along an entire tree structure back and forth >> efficiently. >> >> >> On Monday, August 29, 2016 at 4:28:46 PM UTC-6, Ben Wilson wrote: >>> >>> Can you elaborate on what you'd like out of a zipper library not found >>> in Stream/Enum? >>> >>> On Monday, August 29, 2016 at 10:45:55 AM UTC-4, OvermindDL1 wrote: >>>> >>>> Awesome! Been wanting a good Elixir Zipper library, the others I'd >>>> found were for Erlang and had different usages. :-) >>>> >>>> Easy to do it yourself though of course, but libraries also help >>>> document usage instead of just using a {[],[]} as a zipper. :-) >>>> >>>> On Monday, August 29, 2016 at 5:49:10 AM UTC-6, Brian Bugh wrote: >>>>> >>>>> Thanks for the great idea! I forgot about zippers. This was exactly >>>>> what I needed. There was a zipper tree library on hex but it was overkill >>>>> for what I needed, so I made my own: https://hex.pm/packages/z >>>>> ipper_list >>>>> >>>>> On Wednesday, August 24, 2016 at 10:00:29 AM UTC-5, OvermindDL1 wrote: >>>>>> >>>>>> Instead of holding 'data' and a 'dataptr' you could make the `data` >>>>>> data structure a zipper where the location of the zipper is the ptr. >>>>>> Would >>>>>> be very efficient. >>>>>> >>>>>> https://en.wikipedia.org/wiki/Zipper_(data_structure) >>>>>> >>>>>> There might even be a library or two on it on hex.pm. :-) >>>>>> >>>>>> >>>>>> On Tuesday, August 23, 2016 at 5:31:11 PM UTC-6, Brian Bugh wrote: >>>>>>> >>>>>>> Ah you're right! Very clever. That's awesome, thanks for explaining. >>>>>>> >>>>>>> The implementation I posted was doing the < and > commands >>>>>>> backwards, because I was thinking about it being cheaper to prepend to >>>>>>> lists instead of appending, but there's no way around it. >>>>>>> >>>>>>> The Wikipedia article about BF isn't very helpful. I've written an >>>>>>> interpreter successfully in Ruby, but I'm doing a clean room >>>>>>> implementation >>>>>>> of it in Elixir so I can't "cheat". >>>>>>> >>>>>>> The < and > commands assume you have a data pointer list of infinite >>>>>>> size, 0 based. Something like Array.new(n, 0) in Ruby. So if you run a >>>>>>> BF >>>>>>> program like *<<<<<<+*, you'd end up with a data structure of [1, >>>>>>> 0, 0, 0, 0, 0], hence the required list manipulation. >>>>>>> >>>>>>> - Brian >>>>>>> >>>>>>> On Aug 23, 2016, at 4:24 PM, Torben Hoffmann <torben...@gmail.com> >>>>>>> wrote: >>>>>>> >>>>>>> Hi Brian, >>>>>>> >>>>>>> Looking at what your code does and what I suggested I don't see a >>>>>>> difference. >>>>>>> >>>>>>> Your command/2 will subtract 1 from dataprt and if dataptr < 0 then >>>>>>> you will reset dataptr to 0 and pop data. >>>>>>> >>>>>>> My solution took the liberty of saying "if dataptr=0 (before you >>>>>>> subtract) we have to pop data otherwise we subtract one from dataprt and >>>>>>> that is it". >>>>>>> That should do the same as your code, because if dataptr=0 when you >>>>>>> run your command/2 function you will end up with dataptr=-1 before >>>>>>> calling >>>>>>> do_trim, which will reset it to 0 and pop data. >>>>>>> >>>>>>> Reading the Brianfuck page on Wikipedia seems to suggest that the < >>>>>>> command should only decrement the dataptr, so I am not sure what the pop >>>>>>> you are doing with tl is good for. But then again, Brainfuck is not >>>>>>> supposed to be easy to understand... >>>>>>> >>>>>>> I actually can't believe that I am going to write the following... >>>>>>> ;-) >>>>>>> >>>>>>> I would consider using two lists to hold the array of cells. Say, >>>>>>> left and right fields in your state. >>>>>>> Initially we would have left=[] and right= "a bunch of zeros". >>>>>>> Then we get something like this >>>>>>> >>>>>>> def command("<", state = %{left: []}) do >>>>>>> state # we silently do nothing when moving too far >>>>>>> end >>>>>>> >>>>>>> def command("<", state = %{left: [h|t]} do >>>>>>> %{state | left: t, right: [h | state.right]} >>>>>>> end >>>>>>> >>>>>>> # we always leave one element in right as that is the end of our >>>>>>> world >>>>>>> def command(">", state = %{right: [_]} do >>>>>>> state >>>>>>> end >>>>>>> >>>>>>> def command(">", state = %{right: [h|t]}) do >>>>>>> %{state | left: [h | state.left], right: t} >>>>>>> end >>>>>>> >>>>>>> def command("+", state = %{right: [h|t]}) do >>>>>>> %{state | right: [ h+1 | t ]} # should probably do some overflow >>>>>>> handling if we want to keep having a cell as a byte >>>>>>> end >>>>>>> ... >>>>>>> def command("[", state = %{right: [0|t]}) do >>>>>>> state >>>>>>> end >>>>>>> >>>>>>> def command("[", state) do >>>>>>> # this is where the fun starts, now we need to enter a mode >>>>>>> where we loop the code >>>>>>> # until we have balanced out [ and ]. >>>>>>> ... >>>>>>> end >>>>>>> >>>>>>> And already here I can see that using command with just two >>>>>>> arguments will probably be hurtful in the long run. >>>>>>> Perhaps the function should be called interpret and take a list of >>>>>>> chars as the first argument and simply peel off the first element. >>>>>>> When we reach a loop we have to start accumulating the commands in >>>>>>> case the loop has to run more than once. >>>>>>> But if we call a loop function that executes commands and return the >>>>>>> collected loop body as well as an updated state, then we simply have to >>>>>>> check the value at the data pointer when the loop has been collected. >>>>>>> If it >>>>>>> is zero we throw away the collected program, if not we re-insert the >>>>>>> collected program in front of the rest of the program and call interpret >>>>>>> again. >>>>>>> >>>>>>> Now I am almost tempted to go write the full interpreter, but >>>>>>> luckily for me it is late at my end right now, so I'll have to see if I >>>>>>> am >>>>>>> up for it tomorrow!! >>>>>>> >>>>>>> Cheers, >>>>>>> Torben >>>>>>> >>>>>>> >>>>>>> On 23 August 2016 at 15:55, Brian Bugh <brai...@gmail.com> wrote: >>>>>>> >>>>>>>> It turns out that I think my assumption about how the BF >>>>>>>> interpreter works is wrong, but I want to continue this discussion >>>>>>>> because >>>>>>>> while my interpreter may be bad, I still don't know the answer to my >>>>>>>> Elixir >>>>>>>> question. :) >>>>>>>> >>>>>>>> Torben - thanks for the idea! I did try it with function pattern >>>>>>>> matching. However, the actual command is dataptr - 1 , so that >>>>>>>> needs to run first. *Then* if the dataptr is less than 0, the >>>>>>>> other check runs. The way you've suggested it in the first command >>>>>>>> assumes that the < command has already been run. :) >>>>>>>> >>>>>>>> Here's what I tried with pattern matching, which is even less >>>>>>>> comprehensible than that simple little if statement at the top. >>>>>>>> >>>>>>>> def command("<", state = %{data: data, dataptr: dataptr}) when >>>>>>>> is_list(data) and is_integer(dataptr) and dataptr >= 0 do >>>>>>>> do_trim(%{state | dataptr: dataptr - 1}) >>>>>>>> end >>>>>>>> >>>>>>>> def do_trim(state = %{data: data, dataptr: dataptr}) when >>>>>>>> dataptr < 0 do >>>>>>>> %{state | dataptr: 0, data: tl(data) } >>>>>>>> end >>>>>>>> >>>>>>>> def do_trim(state = %{data: data, dataptr: dataptr}), do: state >>>>>>>> >>>>>>>> Having been a programmer for 23 years, I'm a huge fan of writing >>>>>>>> code that my tired, burned out, >>>>>>>> working-on-the-weekend-because-release-date-is-Monday >>>>>>>> future self won't have to think about too hard to understand. >>>>>>>> >>>>>>>> Nothing I've come up with so far is as clear as that simple little >>>>>>>> if statement. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On Tuesday, August 23, 2016 at 8:24:51 AM UTC-5, Torben Hoffmann >>>>>>>> wrote: >>>>>>>>> >>>>>>>>> Hi Brian, >>>>>>>>> >>>>>>>>> How about this? >>>>>>>>> >>>>>>>>> def command("<", state= %{dataprt: 0}) do >>>>>>>>> {:ok, %{state | data: tl(data)}} >>>>>>>>> end >>>>>>>>> >>>>>>>>> def command("<", state) do >>>>>>>>> {:ok, %{state | dataprt: state.dataptr - 1}} >>>>>>>>> end >>>>>>>>> >>>>>>>>> By using the pattern matching in the function clauses you get code >>>>>>>>> that reads like your description of the problem. >>>>>>>>> I.e., if the dataptr is zero pop the data stack otherwise >>>>>>>>> decrement the dataptr. >>>>>>>>> >>>>>>>>> Cheers, >>>>>>>>> Torben >>>>>>>>> >>>>>>>>> On 23 August 2016 at 15:08, Brian Bugh <brai...@gmail.com> wrote: >>>>>>>>> >>>>>>>>>> Here's another ugly one I came up with (with the complete >>>>>>>>>> function for reference) that is even worse. >>>>>>>>>> >>>>>>>>>> def command("<", state = %{data: data, dataptr: dataptr}) when >>>>>>>>>> is_list(data) and is_integer(dataptr) and dataptr >= 0 do >>>>>>>>>> dataptr = dataptr - 1 >>>>>>>>>> >>>>>>>>>> state = case state do >>>>>>>>>> %{dataptr: dataptr} when dataptr < 0 -> %{state | dataptr: 0, >>>>>>>>>> data: tl(data) } >>>>>>>>>> _ -> state >>>>>>>>>> end >>>>>>>>>> >>>>>>>>>> {:ok, %{state | dataptr: dataptr, data: data}} >>>>>>>>>> end >>>>>>>>>> >>>>>>>>>> Usually when I get stuck like this it means I'm overthinking >>>>>>>>>> something. >>>>>>>>>> >>>>>>>>>> Any suggestions? >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Tuesday, August 23, 2016 at 7:51:22 AM UTC-5, Brian Bugh wrote: >>>>>>>>>>> >>>>>>>>>>> For fun and profit, I am writing a Brainf**k interpreter in >>>>>>>>>>> Elixir. >>>>>>>>>>> >>>>>>>>>>> In one particular case, a data pointer should be decremented, >>>>>>>>>>> and if it's less than 0, it should be set to 0 and the top of the >>>>>>>>>>> data >>>>>>>>>>> stack should be popped off. >>>>>>>>>>> >>>>>>>>>>> I assumed that I should write something like this (which passes >>>>>>>>>>> my : >>>>>>>>>>> >>>>>>>>>>> dataptr = dataptr - 1 >>>>>>>>>>> >>>>>>>>>>> if dataptr < 0 do >>>>>>>>>>> dataptr = 0 >>>>>>>>>>> data = tl data >>>>>>>>>>> end >>>>>>>>>>> >>>>>>>>>>> but I get a compiler warning when I do this. It suggested that I >>>>>>>>>>> use the assignment form of *if*, which is fine, but I can't >>>>>>>>>>> find an elegant way to write the code now. This is what I came up >>>>>>>>>>> with, >>>>>>>>>>> which seems uglier and unnecessarily verbose for future readers. >>>>>>>>>>> The first >>>>>>>>>>> form above is much more readable. >>>>>>>>>>> >>>>>>>>>>> [dataptr | data] = if dataptr < 0 do >>>>>>>>>>> [0 | tl data] >>>>>>>>>>> else >>>>>>>>>>> [dataptr | data] >>>>>>>>>>> end >>>>>>>>>>> >>>>>>>>>>> Is there a better Elixir-way to write this? >>>>>>>>>>> >>>>>>>>>> -- >>>>>>>>>> You received this message because you are subscribed to the >>>>>>>>>> Google Groups "elixir-lang-talk" group. >>>>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>>>> send an email to elixir-lang-ta...@googlegroups.com. >>>>>>>>>> To view this discussion on the web visit >>>>>>>>>> https://groups.google.com/d/msgid/elixir-lang-talk/bfcb8bdf- >>>>>>>>>> aede-4955-ac50-a6ac6dde3e5a%40googlegroups.com >>>>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/bfcb8bdf-aede-4955-ac50-a6ac6dde3e5a%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>>>> . >>>>>>>>>> >>>>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> -- >>>>>>>>> http://www.linkedin.com/in/torbenhoffmann >>>>>>>>> @LeHoff >>>>>>>>> >>>>>>>> -- >>>>>>>> You received this message because you are subscribed to the Google >>>>>>>> Groups "elixir-lang-talk" group. >>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>> send an email to elixir-lang-ta...@googlegroups.com. >>>>>>>> To view this discussion on the web visit >>>>>>>> https://groups.google.com/d/msgid/elixir-lang-talk/743c6c3e- >>>>>>>> 0def-4cf6-8dee-2338a19c0ef6%40googlegroups.com >>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/743c6c3e-0def-4cf6-8dee-2338a19c0ef6%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>> . >>>>>>>> >>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> -- >>>>>>> http://www.linkedin.com/in/torbenhoffmann >>>>>>> @LeHoff >>>>>>> >>>>>>> -- >>>>>>> You received this message because you are subscribed to a topic in >>>>>>> the Google Groups "elixir-lang-talk" group. >>>>>>> To unsubscribe from this topic, visit https://groups.google.com/d/to >>>>>>> pic/elixir-lang-talk/_BPxgTc4VGM/unsubscribe. >>>>>>> To unsubscribe from this group and all its topics, send an email to >>>>>>> elixir-lang-ta...@googlegroups.com. >>>>>>> To view this discussion on the web visit >>>>>>> https://groups.google.com/d/msgid/elixir-lang-talk/CABf3pCmn >>>>>>> vMWt3xqvOC3Yim06XpLoSRkqzYqjPUoSp1HurGQbuw%40mail.gmail.com >>>>>>> <https://groups.google.com/d/msgid/elixir-lang-talk/CABf3pCmnvMWt3xqvOC3Yim06XpLoSRkqzYqjPUoSp1HurGQbuw%40mail.gmail.com?utm_medium=email&utm_source=footer> >>>>>>> . >>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>> >>>>>>> -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-talk" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to elixir-lang-talk+unsubscr...@googlegroups.com > <javascript:_e(%7B%7D,'cvml','elixir-lang-talk%2bunsubscr...@googlegroups.com');> > . > To view this discussion on the web visit https://groups.google.com/d/ > msgid/elixir-lang-talk/30f5bbd0-b3d3-4309-bf35- > 6707cf3d723e%40googlegroups.com > <https://groups.google.com/d/msgid/elixir-lang-talk/30f5bbd0-b3d3-4309-bf35-6707cf3d723e%40googlegroups.com?utm_medium=email&utm_source=footer> > . > For more options, visit https://groups.google.com/d/optout. > -- *José Valim* www.plataformatec.com.br Skype: jv.ptec Founder and Director of R&D -- You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-talk+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/CAGnRm4LJYffw%2B4jjHp0qJzRH8c09gc2E2t%3DTJTcaALBtnn-aAA%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.