Fl4m3Ph03n1x

Fl4m3Ph03n1x

How to use streams with char lists?

Background

I have the following code, which takes a string, converts it to a charlist and then maps over it.

defmodule RotationalCipher do
  @alphabet_size 26

  defguard is_lower?( char ) when char in ?a..?z
  defguard is_upper?( char ) when char in ?A..?Z

  def rotate(text, shift) do
    text
    |> String.to_charlist()
    |> Enum.map( &spin(&1, shift) )
    |> to_string()
  end

  defp spin( char, shift ) when is_lower?( char ), do: ?a + rem( char - 71 + shift, @alphabet_size )
  defp spin( char, shift ) when is_upper?( char ), do: ?A + rem( char - 39 + shift, @alphabet_size )
  defp spin( char, _ ), do: char
end

Problem

Using Enum is all nice but there is a performance benefit in using streams. As in, according to Elixir In Action, I should only use Enum at the very end to force everything into coming together. Doing it before only makes things slower.

Now you will say “this is a simple app, no need to optimize.” But consider that we are ciphering the entire Bible or any of it’s variants. It’s quite a big book and ciphering it via streams would offer a real benefit.

Question

So my question is:

  • Can this example be adapted to use Streams and only use 1 Enum at the end?

Marked As Solved

kokolegorille

kokolegorille

Maybe this could help for processing text in parallel

Also Liked

NobbZ

NobbZ

Don’t stream just because you heard that it is faster. This is not true!

In many cases streams make it slower!

Please benchmark your use case, before blindly applying a stream.

In a case where you iterate a single time, a stream will probably cost, as you have some overhead during dispatching.

If you have a certain number of stages, you might consider a stream, but should still not blindly use it.

In my opinion, streams do especially play their powers when you would do a lot of “shape shifting” stages, which would flatten a given enum or might drop elements inbetween. A stream here helps a lot to reduce time spent with building lists.

If you want to process a string by char and have concerns about converting the string to a list, map over it andconvert it back again, you should probably use a recursive function that matches on the codepoints and builds a new string in the accumulator, then you might receive some additional optimisation powers from the beam, but beware composed characters!

def f(str, acc \\ <<>>)
def f(<<>>, acc), do: acc
def f(<<c::utf8, str::binary>>, acc), do: f(str, acc <> <<c + 1>>)
dimitarvp

dimitarvp

Doesn’t that work?

StringIO.open(text)
|> elem(1)
|> IO.stream(:line)
|> Stream.map(&spin(&1, shift))     # your processing here
# ... more of your processing here
|> Enum.join

The last line directly combines all string pieces into a big string. But you can always use any Enum function istead of that one, depending on your desired end result.

NobbZ

NobbZ

I thought similar to you about a year ago. I learned the hard way during advent of code that streams have a cost.

I learned by benchmarks that they only give a benefit in the above described situations and on very large input that would trigger many GCs when processed using Enum.

A stream always involves additional state keeping, multiple layers of dynamic dispatch, etc. Situation might be totally different when we had a statically typed language that were able to dispatch most of the calls during compile time.

You can learn about my last year’s experience with advent of code when searching this forum.

Where Next?

Popular in Questions Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement