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

qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Other popular topics Top

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 29377 241
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
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 52341 488
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement