Skip to content

Instantly share code, notes, and snippets.

@Kavec
Created October 25, 2015 04:24
Show Gist options
  • Save Kavec/f6664713097d206e7046 to your computer and use it in GitHub Desktop.
Save Kavec/f6664713097d206e7046 to your computer and use it in GitHub Desktop.
#!/usr/bin/env elixir
defmodule Examples do
import Kernel, except: [@: 1]
defmacro __using__(_opts) do
quote do
import Kernel, except: [@: 1]
import Examples, only: [@: 1]
defmodule Examples do
import Agent, only: [
start_link: 2, get: 2, update: 2, get_and_update: 2]
start_link(fn -> %{exs: [], max: 0} end, name: __MODULE__)
def add(name, desc) do
update(__MODULE__, &(
%{exs: [{name, desc} | &1.exs],
max: max(&1.max, String.length(desc))}))
end
{:ok, pid} = start_link(fn -> 0 end, [])
@uids pid
def uid do
Agent.get_and_update(@uids, &({&1, &1+1}))
end
defp reverse(%{exs: exs, max: max}), do: {Enum.reverse(exs), max}
defp print({exs, max}) do
exs
|> Enum.each fn ex ->
{{mod, func}, desc} = ex
desc = String.ljust("#{desc}", max)
[outc] = apply(mod, func, []) # Pop outc out of the list returned
IO.puts "#{desc} -> #{inspect outc}"
end
end
def run, do: get(__MODULE__, &(&1)) |> reverse |> print
end
end
end
defmacro @({:eg, _, body}) do
desc = Macro.to_string(body |> hd)
func = "ex_#{body |> hd |> elem 0}"
quote bind_quoted: [
func: func,
desc: Macro.escape(desc),
body: Macro.escape(body, unquote: true)] do
fid = func <> "_#{inspect __MODULE__.Examples.uid}" |> String.to_atom
__MODULE__.Examples.add({__MODULE__, fid}, desc)
# INFO: When writing macros, unquote(fid) syntax is no bueno
#
# As an example, @eg num_to_list(10) tries to write
#
# `def ex_num_to_list_0 do`
#
# in for the AST, but fails with the message
#
# `invalid syntax in def :ex_num_to_list_0`
#
# What it actually wants (in order to make this compile and work) is the
# code fragment `def ex_num_to_list_0() do`
def unquote(fid)() do
unquote(body)
end
end
end
defmacro @(expr) do
quote do: Kernel.@(unquote(expr))
end
end
# ------------------------------ External ------------------------------ #
defmodule Learn do
@moduledoc """
###Learn
======================
Small exercises used to obtain ILR 1 competency with Elixir; This constitutes
solutions to the first ten problems of elixirexperience.com
"""
use Examples
@spec num_to_list(number) :: list
def num_to_list(n) when is_number(n), do: Enum.join(1..n, ",")
@eg num_to_list(10) # -> "1,2,3,4,5,6,7,8,9,10"
@spec fib(number) :: number
defp _fib(1), do: 1
defp _fib(2), do: 1
defp _fib(n), do: _fib(n - 1) + _fib(n - 2)
def fib(n) when is_number(n) do
1..n |> Enum.map &_fib/1
end
@eg fib(5) # -> "[1, 1, 2, 3, 5]"
@spec concat(binary, binary) :: binary
def concat(a, b), do: a <> b
@eg concat("foo", "bar") # -> "foobar"
@spec palindrome?(binary) :: boolean
def palindrome?(a) when not is_binary(a), do: false
def palindrome?(a) do
a = a
|> String.replace(~r/\P{L}/u, "")
|> String.downcase
a == String.reverse a
end
@eg palindrome?("A man, a plan, a canal, Panama!") # -> true
@eg palindrome?("A man, a plan, a canoe, Panama!") # -> false
@spec anagram?(binary, binary) :: boolean
def anagram?(a, b) when not (is_binary(a) and is_binary(b)), do: false
def anagram?(a, b) do
a = a |> String.to_char_list |> Enum.sort
b = b |> String.to_char_list |> Enum.sort
a == b
end
@eg anagram?("rose", "sore") #=> true
@eg anagram?("rose", "sorb") #=> false
@spec extract_bytes(binary, number) :: binary
def extract_bytes(b, n), do: binary_part(b, 0, n)
# Alternate implementation:
# <<extr::binary-size(n), _rest::binary>> = b; extr
@eg extract_bytes(<<102, 111, 111, 32, 98, 97, 114, 0, 0, 0, 1>>, 4) #=> "foo "
# This is a poorly specified function; I think it's supposed to be from A-Z or
# a-z and I find which character is not in there?
#
# Nope, tests show that the example is misleading. We want numbers, not characters
@spec find_missing_char(char_list) :: number|nil
def find_missing_char(chlist) do
chlist
|> Enum.sort
|> to_char_list
|> (fn
[ch | _] = chl when ch in ?A..?Z -> Enum.to_list(?A..?Z) -- chl
[ch | _] = chl when ch in ?a..?z -> Enum.to_list(?a..?z) -- chl
end).()
|> List.first
end
@eg find_missing_char('ZCGBMHFJYTODIUQARVEWPLNKX') #=> ?S
@eg find_missing_char('ZCGBMHFJYTODIUQSRVEWPLNKX') #=> ?A
@eg find_missing_char('ROPHITNBXLUYJSQKGCFEWDZMV')
@eg find_missing_char('abcdefghijklmnopqrstuvwxyz') #=> nil
@spec checksum(binary) :: number
def checksum(str), do: checksum(0, str)
def checksum(lrc, <<>>), do: lrc
def checksum(lrc, <<b::size(8)>> <> rest) do
import Bitwise
lrc |> bxor(b) |> checksum(rest)
end
@eg checksum("Elixir is fun.") #=> 106
@spec join(tuple, binary) :: binary
def join(tup, sep), do: tup |> Tuple.to_list |> Enum.join(sep)
@eg join({1,2,3}, " ") #=> "1 2 3"
end
# Run all the examples defined by @eg and print the results to stdout
Learn.Examples.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment