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 <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/topic/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/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 elixir-lang-talk+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-talk/5e31149b-e70c-431a-9522-1a7b9f9d70c8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to