broeman

broeman

I am using Project Euler this summer for learning to solve problems in Elixir.
The fourth problem is about palindrome product of 2/3 digits,
and I create a map of all the sums (probably a better way to keep it flatten, and I do look for the palindromes afterwards):

Enum.map(upper..lower, fn x -> Enum.map(upper..x, fn y -> x * y end) end)
|> List.flatten

I am used to, from iterative design, to return early, like:

for x <- upper..lower
    for y <- upper..x
      if palindrome?(x*y) then return x*y

which sounds more efficient to me, as finding the largest palindrome must be made by larger numbers in the product. I could put an if-statement in the map and “filter” the palindromes out faster, but it still have to go through the whole map(s).

Is there a better way? I would like to know the functional programming approach to this problem. Hope it makes sense :smile:

Showing Posts 1 to 7

OvermindDL1

OvermindDL1

Well translating this directly would just be:

def step_x(upper, lower, x) when x<lower, do: nil
def step_x(upper, lower, x) do
  case step_y(upper, x, upper) do
    nil -> step_x(upper, lower, x-1)
    result -> result
  end
end

def step_y(upper, x, y) when <x, do: nil
def step_y(upper, x, y), do: if palindrome?(x*y), do: x*y, else: step_y(upper, x, y-1)

Or something like that, I’ve not tested it, but that is the direct conversion.

Azolo

Azolo

I mean, I wouldn’t really call that approach functional. :stuck_out_tongue_winking_eye:

I just solved it using a more functional approach if you want to see mine… I feel bad posting solutions to Project Euler though. :persevere:

net

net

You can do

is_palindrome? = fn (n) -> s = to_string(n); s == String.reverse(s) end

try do
  for x <- 999..100, y <- 999..x, n = x * y, is_palindrome?.(n), do: throw(n)
catch 
  n -> n
end

which is the equivalent of your second example.

However, that will not find the largest palindrome. To do that you want

Enum.reduce_while 999..100, 0, fn
  x, highest when highest >= x * 999 -> {:halt, highest}
  x, highest ->
    highest =
      Stream.map(999..x, &(x * &1))
      |> Enum.reduce(highest, &(&1 > &2 && is_palindrome?.(&1) && &1 || &2))
    {:cont, highest}
end
peerreynders

peerreynders

It may be idea to point out that you are throwing a value - which merely classifies as a non-local return which is quite different from raising an error or exception (but still gets you into trouble if no one is there to catch it).

PS: Just a general note - code inside the protected area of the try cannot be subject to last call optimization (TCO). So in general you want to keep your “stay there” as brief as possible or if necessary be a little more vigilant about your call stack use.

sasajuric

sasajuric

Author of Elixir In Action

You could use Stream for that:

upper..lower
|> Stream.flat_map(fn x -> Stream.map(x..lower, &{x, &1}) end)
|> Stream.filter(fn {x,y} -> palindrome?(x*y) end)
|> Enum.take(1)

This code will stop on the first detected palindrome (which I believe is not necessarily the largest palindrome).

Qqwy

Qqwy

TypeCheck Core Team

There also is Enum.reduce_while which can be used to do short-circuiting but avoid the overhead you get from using Streams.

upper = 999
lower = 100
(upper..lower)
|> Enum.flat_map(fn x -> Enum.map(x..lower, &{x, &1}) end)
|> Enum.reduce_while(nil, fn {x, y}, _ ->
  if palindrome?(x * y) do
    {:halt, x * y}
  else
    {:cont, nil}
  end
end)
)

Just like @sasajuric’s method, this will not neccesarily return the largest palindrome, because two medium-sized numbers might have a larger product than a large number with a small number.

broeman

broeman OP

Yes, you’re right according to the largest palindrome.

I was just wondering about how to exit early, and you all gave great answers, I can ponder upon :smile:

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
widianto
I think I’ve found a small improvement I could contribute to &lt;%= web_namespace %&gt;.CoreComponents (installer/templates/phx_web/compo...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews