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/zipper_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 <[email protected]> 
>>>> 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 <[email protected]> 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 <[email protected]> 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 [email protected].
>>>>>>> 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 [email protected].
>>>>> 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/topic/elixir-lang-talk/_BPxgTc4VGM/unsubscribe
>>>> .
>>>> To unsubscribe from this group and all its topics, send an email to 
>>>> [email protected].
>>>> To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/elixir-lang-talk/CABf3pCmnvMWt3xqvOC3Yim06XpLoSRkqzYqjPUoSp1HurGQbuw%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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-talk/f2954d8a-d458-4e7d-abe7-567ab87cd3dd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to