You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
iex(4)>hFile.readdefread(path)@specread(Path.t())::{:ok,binary()}|{:error,posix()}Returns{:ok,binary},wherebinaryisabinarydataobjectthatcontainsthecontentsofpath,or{:error,reason}ifanerroroccurs.Typical error reasons:
• :enoent-thefiledoesnotexist
• :eacces-missingpermissionforreadingthefile,or for searching
one of the parent directories
• :eisdir-thenamedfileisadirectory
• :enotdir-acomponentofthefilenameisnotadirectory;onsomeplatforms,:enoent is returned instead
• :enomem-thereisnotenoughmemoryforthecontentsofthefileYou can use:file.format_error/1togetadescriptivestringoftheerror.
Another example of File.read! but this time with !
iex(5)>hFile.read!defread!(path)@specread!(Path.t())::binary()Returns a binarywiththecontentsofthegivenfilename,orraisesaFile.Error exception if an error occurs.
iex(8)>hString.split/1defsplit(binary)@specsplit(t())::[t()]delegate_to: String.Break.split/1Divides a stringintosubstringsateachUnicodewhitespaceoccurrencewithleadingandtrailingwhitespaceignored.Groupsofwhitespacearetreatedasasingleoccurrence.Divisionsdonotoccuronnon-breakingwhitespace.## Examplesiex>String.split("foo bar")["foo","bar"]iex>String.split("foo"<><<194,133>><>"bar")["foo","bar"]iex>String.split(" foo bar ")["foo","bar"]iex>String.split("no\u00a0break")["no\u00a0break"]
It will return the use-case with examples for the split/1 function
Lets test out the split/1 function
sample_string="123\n456\n789\n"
"123\n456\n789\n"
# split the string on \n using a regular expression (Will hav an empty string at the end)String.split(sample_string,~r/\n/)
["123", "456", "789", ""]
# because there was an extra \n at the end of our sample_string# we ended up with a extra empty string in our list# to remove that we will do the followingString.split(sample_string,~r/\n/,trim: true)
# Lets implement all this livedefmoduleDictionarydo@words_list_pathPath.expand("../dictionary/assets",__DIR__)|>Path.join("words.txt")defword_listdo@words_list_path|>File.read!()|>String.split(~r/\n/,trim: true)enddefrandom_worddoEnum.random(word_list())endend# ========================================================words=Dictionary.word_list()
iex(4)>hEnum.randomdefrandom(enumerable)@specrandom(t())::element()Returns a randomelementofanenumerable.Raises Enum.EmptyErrorifenumerableisempty.ThisfunctionusesErlang's :rand module (:rand) to calculate the random value.Check its documentation for setting a different random algorithm or a differentseed.The implementation is based on the reservoir sampling(https://en.wikipedia.org/wiki/Reservoir_sampling#Relation_to_Fisher-Yates_shuffle)algorithm. It assumes that the sample being returned can fit into memory; theinput enumerable doesn't have to,asitistraversedjustonce.Ifarangeispassedintothefunction,thisfunctionwillpickarandomvaluebetweentherangelimits,withouttraversingthewholerange(thusexecutinginconstanttimeandconstantmemory).## ExamplesTheexamplesbelowusethe:exssspseudorandomalgorithmsinceit's the defaultfrom Erlang/OTP 22: # Although not necessary, let's seedtherandomalgorithmiex>:rand.seed(:exsss,{100,101,102})iex>Enum.random([1,2,3])2iex>Enum.random([1,2,3])1iex>Enum.random(1..1_000)309
using our new random_word function
Dictionary.random_word()
"monetary"
Section: EPIC0001
There are two ways of specifying an atom in Elixir. The first is to prefix a name or an operator with a colon. Here are some atoms using that notation:
atoms=[:cat,:puppy_dog,:>=]
[:cat, :puppy_dog, :>=]
Sometimes you need to create atoms that contain characters that aren't allowed in normal names. Do this by enclosing them in double quotes:
crazy_atoms=[:"cat-dog",:"now is the time",:"!@$%^&UIO"]# looped_atoms =# Enum.each(# crazy_atoms,# fn atom -> IO.inspect(IEx.Info.info(atom), label: "atom type") end# )looped_atoms=foratom<-crazy_atomsdoIO.puts("*. atom name: #{inspect(atom)}")IO.puts("*. is atom?: #{inspect(is_atom(atom))}\n")end
*. atom name: :"cat-dog"
*. is atom?: true
*. atom name: :"now is the time"
*. is atom?: true
*. atom name: :"!@$%^&UIO"
*. is atom?: true
[:ok, :ok, :ok]
This format also allows you to embed the result of evaluating code in your atom names:
a=99
99
:"next-number: #{a+1}"
:"next-number: 100"
Section: EPIC0003
Send & receive processes
defmoduleProcsdodefreceiving(count)doreceivedo{:crash,reason}->exit(reason)# we will quit without a recursive call{:quit}->IO.inspect({:quit},label: "Program quit")# pattern matching recursively{:add,n}->IO.inspect(receiving(count+n),label: "{:add, n}")msg->IO.puts("#{count}: Hello #{inspect(msg)}")# using tail recursionreceiving(count+1)receiving(count)endendendpid=spawn_link(Procs,:receiving,[0])# sending a message to the pidsend(pid,"Hola")# to show our process endedsend(pid,"Again")send(pid,[1,2,3])send(pid,17.0)send(pid,:alias_one_11)# running a background process that is maintaining its own statesend(pid,{:add,17})send(pid,{:quit})# process should not be alive after :quitsend(pid,{:crash,:normal})Process.alive?(pid)
iex(1)>hAgent.start_linkdefstart_link(fun,options\\[])@specstart_link((()->term()),GenServer.options())::on_start()Starts an agentlinkedtothecurrentprocesswiththegivenfunction.Thisisoftenusedtostarttheagentaspartofasupervisiontree.Oncetheagentisspawned,thegivenfunctionfunisinvokedintheserverprocess,andshouldreturntheinitialagentstate.Note that start_link/2doesnotreturnuntilthegivenfunctionhasreturned.## OptionsThe:nameoptionisusedforregistrationasdescribedinthemoduledocumentation.Ifthe:timeout option is present,theagentisallowedtospendatmostthegivennumberofmillisecondsoninitializationoritwillbeterminatedandthestartfunctionwillreturn{:error,:timeout}.If the :debugoptionispresent,thecorrespondingfunctioninthe:sysmodule(:sys) will be invoked.Ifthe:spawn_opt option is present,itsvaluewillbepassedasoptionstotheunderlyingprocessasinProcess.spawn/4.## Return valuesIftheserverissuccessfullycreatedandinitialized,thefunctionreturns{:ok,pid},wherepidisthePIDoftheserver.Ifanagentwiththespecifiednamealreadyexists,thefunctionreturns{:error,{:already_started,pid}}withthePID of that process.If the given function callback fails,thefunctionreturns{:error,reason}.## Examplesiex>{:ok,pid}=Agent.start_link(fn->42end)iex>Agent.get(pid,fnstate->stateend)42iex>{:error,{exception,_stacktrace}}=Agent.start(fn->raise"oops"end)iex>exception%RuntimeError{message: "oops"}defstart_link(module,fun,args,options\\[])@specstart_link(module(),atom(),[any()],GenServer.options())::on_start()Starts an agentlinkedtothecurrentprocess.Sameasstart_link/2 but a module,function,andargumentsareexpectedinsteadofananonymousfunction;funinmodulewillbecalledwiththegivenargumentsargstoinitializethestate.
We always use a function to manipulate the state inside of an Agent
{:ok,counter}=Agent.start_link(fn->0end)# Agent.get(counter, fn state -> state end)# # update->1# Agent.update(counter, fn state -> state + 1 end)# Agent.get(counter, fn state -> state end)# # update->2# Agent.update(counter, fn state -> state + 1 end)# Agent.get(counter, fn state -> state end)Agent.get_and_update(counter,fnstate->IO.inspect({state,state+1},label: "get_and_update")end)Agent.get_and_update(counter,fnstate->IO.inspect({state,state+1},label: "get_and_update")end)
get_and_update: {0, 1}
get_and_update: {1, 2}
1
Converting our dictionary api by way of refactoring it with Agents
Our next steps... Converting our Dictionary library into an Application
Section: EPIC0004
Project summary up to this point
GenServer is a wrapper for any server:
GenServeris an abstraction that allows us to run code without having to deal with all of the details like:
Life-cycle management
Timeouts
Timeouts
The Gen in GenServer stands for Generic that will wrap any server in the elixirworld
Its an abstraction, simlar to how Enum is an abstraction to a collection & Agents are an abstraction over a process/server that holds state
A GenServer has to distinct API's, because just like the Agent, it has a process on the client for the Agent that uses it but it also has code that uses it in the server processes
These two pieces of code will communicate via messages but live in two different environments (Agent vs GenServer), so the API's are quite distinct.
An example on the implementation
importIEx.Helpers
IEx.Helpers
h(Enum.map())
def map(enumerable, fun)
@spec map(t(), (element() -> any())) :: list()
Returns a list where each element is the result of invoking fun on each
corresponding element of enumerable.
For maps, the function expects a key-value tuple.
## Examples
iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]
iex> Enum.map([a: 1, b: 2], fn {k, v} -> {k, -v} end)
[a: -1, b: -2]