Created
October 25, 2015 04:24
-
-
Save Kavec/f6664713097d206e7046 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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