Trying to level up my Elixir understanding by doing algo/leetCode style problems using Elixir.
As I'm a relatively new programmer (around a year in) and was trained on traditionally OOP languages like Ruby and JS, it's still somewhat hard for me to wrap my head around doing algo questions in a functional paradigm, though I felt I understood the Udemy course I took on Elixir/Phoenix.
I wrote a solution to the LeetCode "valid anagram" problem using Elixir and Repl and wanted to see if people had any ideas for improving/understanding the problem or if there was a best approach way of thinking for this problem.
For an answer, I'd take a code review, a book recommendation or even just suggestions of what I could do differently.
Thank you for your time and hope this (my first question on this site) is clear.
###
Given two strings s and t , write a function to determine if t is an anagram of s.
Example 1:
Input: s = "anagram", t = "nagaram"
Output: true
Example 2:
Input: s = "rat", t = "car"
Output: false
Note:
You may assume the string contains only lowercase alphabets.
###
defmodule Algos do
def is_anagram(str1, str2) do
case String.length(str1) == String.length(str2) do
false ->
IO.puts(false)
true ->
both_trackers(str1, str2)
|> check_trackers
|> IO.puts
end
end
def both_trackers(str1, str2) do
t1 = make_tracker(str1)
t2 = make_tracker(str2)
{t1, t2}
end
def check_trackers({t1, t2}) do
Map.keys(t1)
|> Enum.reduce_while(true, fn x, acc ->
if t1[x] == t2[x], do: {:cont, acc}, else: {:halt, false}
end)
end
def make_tracker(str) do
tracker = String.split(str, "", trim: true)
|> Enum.reduce(%{},
fn x,acc -> Map.merge(acc,
case !!acc[x] do
false ->
%{x => 1}
true ->
%{x => acc[x] + 1}
end
)
end
)
tracker
end
end
Algos.is_anagram("sloop ", "pools")
New elixir has Enum.frequencies, which generates a histogram from an enumerable, which basically solves this problem out of the box:
defmodule Algos do
def anagram?(a, b) do
Enum.frequencies(to_charlist(a)) == Enum.frequencies(to_charlist(b))
end
end
Algos.anagram?("a gentleman", "elegant man") # => true
Algos.anagram?("alice", "bob") # => false
Related
I'm very new to Julia but I've got a some background in Scheme/Rust/F#.
Today I wanted to make yesterday's AoC nicer without an explicit number of nested loops.
I arrived at this working solution, but I don't like the last if. In the languages mentioned above I would call a function (or use a computation expression) that gives me the first result that is not None. For Julia, I expected something to do that. It does, but unexpectedly in an eager fashion.
So When I tried return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended)), that also evaluated the second argument when the first already had a result—and thus crashed.
Is there a macro/lazy version or something that I didn't find? How are you supposed to handle a case like that?
I also thought about (short-circuited) or'ing them together, but I guess Julia's strictness in that matter spoils that.
using DataStructures
function find(r::Array{Int}, n, start = 1, which = nil())::Union{Int,Nothing}
if start <= length(r)
extended = cons(start, which)
with_current = sum(i -> r[i], extended)
if with_current == 2020 && n == 1
return prod(i -> r[i], extended)
else
# Unfortunately no :(
#return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
re = find(r, n, start + 1, which)
if isnothing(re)
return find(r, n - 1, start + 1, extended)
else
re
end
end
end
end
Let me comment more on it why it is not possible given the discussion in the comments.
In Julia function arguments are evaluated eagerly, so Julia evaluates both find(r, n, start + 1, which) and find(r, n - 1, start + 1, extended) before passing them to something function.
Now, with macros you have (I am not writing in a fully general case for simplicity and I hope I got the hygiene right :)):
julia> macro something(x, y)
quote
local vx = $(esc(x))
isnothing(vx) ? $(esc(y)) : vx
end
end
#something (macro with 1 method)
julia> #something 1 2
1
julia> #something nothing 2
2
julia> #something 1 sqrt(-1)
1
julia> #something nothing sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
(in a full-blown version of the macro varargs and Some should be handled to replicate something exactly)
Piqued by Bogumił's answer I wanted to write my first Julia macro. It took some time and numerous attempts to figure out syntax, hygiene and escaping but I'm quite happy now.
I thought it might be worth sharing and provide opportunity for suggestions/improvements.
A lazy #something analog to Base.something
function _something_impl(thing)
:(something($(esc(thing))))
end
function _something_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_impl(rest...))
else
something(evalued)
end
end
end
macro something(things...)
_something_impl(things...)
end
Version without exceptions
As I found exceptions raised from a macro like this not quite suitable, I also made a version that falls back to nothing.
function _something_nothing_impl(thing)
quote
local evaluated = $(esc(thing))
if isa(evaluated, Some)
evaluated.value
else
evaluated
end
end
end
function _something_nothing_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_nothing_impl(rest...))
else
something(evalued)
end
end
end
macro something_nothing(things...)
_something_nothing_impl(things...)
end
Now I guess the recursive middle function could also generated by a macro. :)
Is it possible to build a Python style recursive generator with Elixir? Something like this:
def traverse(parent_dir):
dirs, files = get_dirs_and_files_as_lists(parent_dir)
for d in dirs:
yield from traverse(d)
for f in files:
yield f
For all the files to be processed linearly, without overhead implied by an eager list of indefinite length:
for f in traverse(dir):
process(f)
This, or some working equivalent, should be possible using streams; unfortunately, I have no idea how.
I want something like this, just lazy:
def traverse_eagerly(parent_dir) do
{dirs, files} = get_dirs_and_files_as_lists(parent_dir)
for x <- dirs do
traverse_eagerly(x)
end
|> Enum.concat()
|> Enum.concat(files)
end
The solution appears to be trivial: replace Enum with Stream.
def traverse_lazily(parent_dir) do
{dirs, files} = get_dirs_and_files_as_lists(parent_dir)
for x <- dirs do
traverse_lazily(x)
end
|> Stream.concat()
|> Stream.concat(files)
end
The following works as expected:
s = traverse_lazily(a_dir_of_choice)
for x <- s, do: whatever_you_please(x)
Very nice of the language. As fine a solution as you would wish for. Unless I'm missing something, that is :) . Comments are welcome!
You do not need Stream here, but if you want, here is it:
defmodule Traverse do
#spec traverse(root :: binary(), yielder :: (binary() -> any())) ::
:ok | {:error, posix()}
def traverse(root, yielder) do
# https://hexdocs.pm/elixir/master/File.html?#ls/1
with {:ok, list} <- File.ls(root) do
list
|> Stream.each(fn file_or_dir ->
if File.dir?(file_or_dir),
do: traverse(file_or_dir, yielder), # TCO
else: yielder.(file_or_dir)
end)
|> Stream.run()
end
end
end
And call it like:
Traverse.traverse(".", &IO.inspect/1)
Like in R:
a <- 2
or even better
a ← 2
which should translate to
a = 2
and if possible respect method overloading.
= is overloaded (not in the multiple dispatch sense) a lot in Julia.
It binds a new variable. As in a = 3. You won't be able to use ← instead of = in this context, because you can't overload binding in Julia.
It gets lowered to setindex!. As in, a[i] = b gets lowered to setindex!(a, b, i). Unfortunately, setindex! takes 3 variables while ← can only take 2 variables. So you can't overload = with 3 variables.
But, you can use only 2 variables and overload a[:] = b, for example. So, you can define ←(a,b) = (a[:] = b) or ←(a,b) = setindex!(a,b,:).
a .= b gets lowered to (Base.broadcast!)(Base.identity, a, b). You can overload this by defining ←(a,b) = (a .= b) or ←(a,b) = (Base.broadcast!)(Base.identity, a, b).
So, there are two potentially nice ways of using ←. Good luck ;)
Btw, if you really want to use ← to do binding (like in 1.), the only way to do it is using macros. But then, you will have to write a macro in front of every single assignment, which doesn't look very good.
Also, if you want to explore how operators get lowered in Julia, do f(a,b) = (a .= b), for example, and then #code_lowered f(x,y).
No. = is not an operator in Julia, and cannot be assigned to another symbol.
Disclaimer: You are fully responsible if you will try my (still beginner's) experiments bellow! :P
MacroHelper is module ( big thanks to #Alexander_Morley and #DanGetz for help ) I plan to play with in future and we could probably try it here :
julia> module MacroHelper
# modified from the julia source ./test/parse.jl
function parseall(str)
pos = start(str)
exs = []
while !done(str, pos)
ex, pos = parse(str, pos) # returns next starting point as well as expr
ex.head == :toplevel ? append!(exs, ex.args) : push!(exs, ex)
end
if length(exs) == 0
throw(ParseError("end of input"))
elseif length(exs) == 1
return exs[1]
else
return Expr(:block, exs...) # convert the array of expressions
# back to a single expression
end
end
end;
With module above you could define simple test "language":
julia> module TstLang
export #tst_cmd
import MacroHelper
macro tst_cmd(a)
b = replace("$a", "←", "=") # just simply replacing ←
# in real life you would probably like
# to escape comments, strings etc
return MacroHelper.parseall(b)
end
end;
And by using it you could probably get what you want:
julia> using TstLang
julia> tst```
a ← 3
println(a)
a +← a + 3 # maybe not wanted? :P
```
3
9
What about performance?
julia> function test1()
a = 3
a += a + 3
end;
julia> function test2()
tst```
a ← 3
a +← a + 3
```
end;
julia> test1(); #time test1();
0.000002 seconds (4 allocations: 160 bytes)
julia> test2(); #time test2();
0.000002 seconds (4 allocations: 160 bytes)
If you like to see syntax highlight (for example in atom editor) then you need to use it differently:
function test3()
#tst_cmd begin
a ← 3
a ← a + a + 3 # parser don't allow you to use "+←" here!
end
end;
We could hope that future Julia IDEs could syntax highlight cmd macros too. :)
What could be problem with "solution" above? I am not so experienced julian so many things. :P (in this moment something about "macro hygiene" and "global scope" comes to mind...)
But what you want is IMHO good for some domain specific languages and not to redefine basic of language! It is because readability very counts and if everybody will redefine everything then it will end in Tower of Babel...
I have a function confirm, which reads IO input, and depending by input, if it's y (yes) or n (no), it returns true/false, otherwise it calls confirm again, until any expected y or n is entered.
#spec confirm(binary) :: boolean
def confirm(question) do
answer = question |> IO.gets() |> String.trim() |> String.downcase()
case(answer) do
"y" -> true
"n" -> false
_ -> confirm(question)
end
end
To test y and n cases it's easy:
assert capture_io([input: "y"], fn -> confirm("Question") end) == "Question"
But I have no idea how to capture IO multiple times to test recursive case, let's say if at first input is "invalid" and then "y". Does elixir has any way to test IO functions like this? Or maybe do you have some suggestions how I could rewrite function to test it easier?
Original question https://elixirforum.com/t/testing-recursive-io-prompt/3715
Thanks for the help.
Untested, but what about just using newline characters?
assert capture_io([input: "foo\nbar\ny"], fn -> confirm("Question") end) == "Question"
Im looking for a function like Pythons
"foobar, bar, foo".count("foo")
Could not find any functions that seemed able to do this, in a obvious way. Looking for a single function or something that is not completely overkill.
Julia-1.0 update:
For single-character count within a string (in general, any single-item count within an iterable), one can use Julia's count function:
julia> count(i->(i=='f'), "foobar, bar, foo")
2
(The first argument is a predicate that returns a ::Bool).
For the given example, the following one-liner should do:
julia> length(collect(eachmatch(r"foo", "bar foo baz foo")))
2
Julia-1.7 update:
Starting with Julia-1.7 Base.Fix2 can be used, through ==('f') below, as to shorten and sweeten the syntax:
julia> count(==('f'), "foobar, bar, foo")
2
What about regexp ?
julia> length(matchall(r"ba", "foobar, bar, foo"))
2
I think that right now the closest built-in thing to what you're after is the length of a split (minus 1). But it's not difficult to specifically create what you're after.
I could see a searchall being generally useful in Julia's Base, similar to matchall. If you don't care about the actual indices, you could just use a counter instead of growing the idxs array.
function searchall(s, t; overlap::Bool=false)
idxfcn = overlap ? first : last
r = findnext(s, t, firstindex(t))
idxs = typeof(r)[] # Or to only count: n = 0
while r !== nothing
push!(idxs, r) # n += 1
r = findnext(s, t, idxfcn(r) + 1)
end
idxs # return n
end
Adding an answer to this which allows for interpolation:
julia> a = ", , ,";
julia> b = ",";
julia> length(collect(eachmatch(Regex(b), a)))
3
Actually, this solution breaks for some simple cases due to use of Regex. Instead one might find this useful:
"""
count_flags(s::String, flag::String)
counts the number of flags `flag` in string `s`.
"""
function count_flags(s::String, flag::String)
counter = 0
for i in 1:length(s)
if occursin(flag, s)
s = replace(s, flag=> "", count=1)
counter+=1
else
break
end
end
return counter
end
Sorry to post another answer instead of commenting previous one, but i've not managed how to deal with code blocks in comments :)
If you don't like regexps, maybe a tail recursive function like this one (using the search() base function as Matt suggests) :
function mycount(what::String, where::String)
function mycountacc(what::String, where::String, acc::Int)
res = search(where, what)
res == 0:-1 ? acc : mycountacc(what, where[last(res) + 1:end], acc + 1)
end
what == "" ? 0 : mycountacc(what, where, 0)
end
This is simple and fast (and does not overflow the stack):
function mycount2(where::String, what::String)
numfinds = 0
starting = 1
while true
location = search(where, what, starting)
isempty(location) && return numfinds
numfinds += 1
starting = location.stop + 1
end
end
one liner: (Julia 1.3.1):
julia> sum([1 for i = eachmatch(r"foo", "foobar, bar, foo")])
2
Since Julia 1.3, there has been a count method that does exactly this.
count(
pattern::Union{AbstractChar,AbstractString,AbstractPattern},
string::AbstractString;
overlap::Bool = false,
)
Return the number of matches for pattern in string.
This is equivalent to calling length(findall(pattern, string)) but more
efficient.
If overlap=true, the matching sequences are allowed to overlap indices in the
original string, otherwise they must be from disjoint character ranges.
│ Julia 1.3
│
│ This method requires at least Julia 1.3.
julia> count("foo", "foobar, bar, foo")
2
julia> count("ana", "bananarama")
1
julia> count("ana", "bananarama", overlap=true)
2