Vale fare un post un po LOLLoso ma in cui dico cose che vorrei— devo dire?
Certo Γ¨ il mio blog; ci sono tanti blog, ma questo Γ¨ il mio blog (quasicit.)
L’altro giorno eravamo in tre (due vecchi e un trentenne+) al bar a pranzo verso l’una. Si parlava di dati e software per gestirli e noi vecchi siamo out; almeno quando entra in gioco ecselβ’.
Poi il tavolino di fianco (quelli piccoli rotondi, avete presente vero?) si libera e viene immediatamente conquistato da due signore (rockzs! , come vedremo subito). Nell’attesa che il cameriere sbaracchi una delle due tira fuori (indovina?) il telefono e comincia a mostrare e raccontare all’altra cosa e come aveva fatto.
C’era rumore e non si sentiva niente ma sono rimasto incantato a guardarla a bocca aperta Molto piΓΉ sexy di quel che dicevano i miei due amici. PerchΓ© non doveva essere la solita galleria di foto o la pagina di Facebook, in quei casi si manda un messaggio con dentro “guarda qua!”. E se non arriva subito la risposta con “bello” e faccine sorridenti si rimanda. Fino a quando serve.
Io che sono ormai fuori non ho il telefono, per scelta. Ma mi affascina, ci vedo il successore del ‘puter di adesso. Ho giΓ visto diverse successioni. Mi sono perso le prime generazioni, anche le schede le ho viste solo al Poli, ma dai mini in poi tutti. Fino all’aiFon (escluso).
Poi ieri trovo questo tweet (non seguo Larry, non so chi l’ha ritwittato) che –come dire– OK, eccolo: Why am I so excited about todayβs big astronomy news? Well let me tell yaβ 1/22.
Larry Γ¨ OK, ha interessi diversi dai miei altrimenti lo followerei
PerΓ² non so se avete visto: 1/22. Ecco, secondo me –secondo me, nΓ¨h— questo Γ¨ un uso di Twitter che non avrei mai immaginato. E sembra che questo sia il parere anche di altri, non solo mio: 1/22?
Purtroppo pare che i blogs stiano morendo; si dice da tanto tempo ma forse questa volta Γ¨ vero per davvero
π€’
Continuo da qui, copio qui, scrollare fino a “Filtering: removing elements from a list”.
Filtraggio: rimuovere elementi da una lista
The two functions allSquares
and allToUpper
[post precedente] produced lists with exactly the same length as their input list. Let’s continue with a string example: given a string, how can we extract all the digits. For example, extractDigits "das43 dffe 23 5 def45"
should return the string "4323545"
, using the function isDigit :: Char > Bool
from Data.Char
to check whether a single character is a digit.
The base case is (as almost always), straightforward: if we get an empty string, we just return the empty string. The recursive case is slightly more complicated than in previous examples: we have to check if the first character is a digit in which case we include it in the result. Otherwise, we ignore it and just call extractDigits
on the tail
of the input list:
extd.hs
import Data.Char
extractDigits :: String > String
extractDigits []
= []
extractDigits (chr : restString)
 isDigit chr = chr : extractDigits restString
 otherwise = extractDigits restString
Prelude> :l extd
[1 of 1] Compiling Main ( extd.hs, interpreted )
Ok, 1 module loaded.
*Main> extractDigits "das43 dffe 23 5 def45"
"4323545"
While the structure of this function definition is still quite close to that of allSquares
and allToUpper
, the most obvious difference is the use of guards to distinguish between two different recursive cases: one that uses chr
, the first element of the input list, and one that disregards that element.
Another example, using the ColourPoint
type again, receives a point
and a radius
in addition to a list of points as arguments, and then, yields a list of all points which are located within the radius around the point passed as the first argument:
inRadius :: ColourPoint > Float > [ColourPoint] > [ColourPoint]
inRadius point radius []
= []
inRadius point radius (p : ps)
 distance point p <= radius = p : inRadius point radius ps
 otherwise = inRadius point radius ps
Riduzione: combinare elementi di una stringa
Another important pattern of recursive functions are reductions, which combine the elements of a collection, such as a list, into a new value. An example is the product
function, which computes the product of all the elements of a given list:
product [x_{1}, x_{2},... , x_{n}] β x_{1} * x_{2} * ... * x_{n}
Using our knowledge about the recursive nature of lists, we can rephrase the task as defining a function product
computing
product (x_{1} : (x_{2}: ... : (x_{n}: [])...) β x_{1} * x_{2} * ... * x_{n}
By fixing a suitable order for the calculation of the products on the right hand side and adding the neutral value of multiplication (i.e. 1), we get the right hand side exactly into the same format as the recursive list representation, replacing : with *
, and []
with the neutral element of multiplication, 1
:
product (x_{1}: (x_{2}: ... : (x_{n}: [])...) β (x_{1} * (x_{2} * ... * (x_{n} * 1)...)
Thus, we can use a recursive function definition following the recursion inherent in the list data structure β this is known as structural recursion. For the base case, we have product
applied to an empty list; i.e., nil. From the above specification of product, it is clear that we have to return 1
(the neutral value) in this case:
product [] = 1
The recursive case is that of a nonempty list, where cons allows us to decompose the list into a head
and a tail
. We multiply the head
with the result of recursively applying product to the tail
of the list. So, overall, we have
product :: Num a => [a] > a
product [] = 1
product (x:xs) = x * product xs
This definition clearly does what we said before. It replaces nil by 1 and cons by multiplication. Reductions like product constitute a common pattern of structurally recursive functions.
Inside reductions. To see how a list traversal proceeds, let us look at the stepwise evaluation of product [3, 5, 6]
, which we write as product (3:(5:(6:[])))
to make the recursive structure of the list explicit:
product (3:(5:(6:[]))) β 3 * product (5:(6:[]))
β 3 * (5 * product (6:[]))
β 3 * (5 * (6 * product []))
β 3 * (5 * (6 * 1))
β 90
A general recursive pattern of reductions. There are many other examples of commonly used reductions. For instance, calculating the sum
of a list is almost identical to the product
function:
sum :: Num a => [a] > a
sum [] = 0
sum (x:xs) = x + sum xs
Calculating the minimum or maximum of a list is also similar. However, in this case, it is not clear what the return value for an empty list ought to be. One option is to leave the case of empty lists undefined and proceed as follows (note that we are using min
as infix operator by enclosing it in backquotes):
minList :: [Int] > Int
minList (x:[]) = x
minList (x:xs) = x `min` minList xs
The first equation applies whenever the argument list has exactly one element. The second, by itself, applies whenever a list has at least one element; so, the two patterns overlap. However, for any given argument, code evaluation tests patterns starting with the first equation and then working its way down. The first equation with a matching pattern will be selected. Hence, if a list has exactly one element, minList
will always pick the first equation; it will only select the second equation if the argument list has more than one element. If the list is empty, the result is undefined; i.e, this version of minList
is a partial function.
Alternatively, we might consider the largest possible value of Int
type a neutral value of the min
function and use it to define the base case. In Haskell, the range of Int
is implementation dependent; hence, we use the Prelude
value maxBound
, which yields the largest Int
value (Strictly speaking, maxBound :: Bounded a => a
is a member of the type class Bounded
and works on a range of types, not just Int
.):
minList :: [Int] > Int
minList [] = maxBound
minList (x:xs) = x `min` minList xs
Looking at these examples, the general pattern for list reductions takes a list of the form
(x_{1} : (x_{2} : ... : (x_{n} : [])...)
and replaces cons (:
) and nil ([]
) with a binary function op
and some starting value n
, respectively, to yield
(x_{1} `op` (x_{2} `op` ... `op` (x_{n} `op` [])...)
More examples of reductions. The result of a reduction may even be another list: in fact, the list append operator (++)
:: [a] > [a] > [a], which we used in First Steps, is an instance of such a function. This becomes obvious when we denote the behaviour of (++)
as follows:
(x_{1} : (x_{2} : ... : (x_{n} : [])...) ++ ys βββ (x_{1} : (x_{2} : ... : (x_{n} : ys)...)
Here, we replace cons (:
) by itself (i.e., it is the binary operator of this reduction) and we replace nil ([]
) by the appended list ys
. With that insight, the definition of (++)
is straightforward (using infix notation for (++)
in the definition of the two equations):
(++) :: [a] > [a] > [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
Given this definition, let us see how it evaluates:
(5:(6:(2:[]))) ++ (4:(2:[])) β 5:((6:(2:[])) ++ (4:(2:[])))
β 5:(6:((2:[]) ++ (4:(2:[]))))
β 5:(6:(2:([] ++ (4:(2:[])))))
β 5:(6:(2:(4:(2:[]))))
Lists in Haskell can be nested; i.e., we can have lists of lists of integer numbers, such as
[[5, 6, 2], [], [4, 2]]
A commonly used Prelude
function that takes a list of lists and reduces it to a list with one nesting level fewer is concat
β an example use is
concat [[5, 6, 2], [], [4, 2]] β [5, 6, 2, 4, 2]
As we can join two lists with the ++
operator, we can write a specification for concat
as follows:
concat [x_{1}, x_{2}, ..., xn] β x_{1} ++ x_{2} ++ ... ++ x_{n}
This translates directly into the recursive pattern for reductions, replacing (:)
by (++)
and []
by itself:
concat (x_{1} : (x_{2} : ... : (x_{n} : [])...) β (x_{1} ++ (x_{2} ++ ... ++ (x_{n} ++ [])...)
We rewrite this specification into Haskell as before, to produce
concat :: [[a]] > [a]
concat [] = []
concat (xs:xss) = xs ++ concat xss
Nota per me: di solito si scrive x:xs
; qui invece si fa il plurale forse perchΓ© devono essere liste, anche se un solo elemento metterlo come lista, cosΓ¬, [8]
.
Finally, let’s look at a slightly trickier function. The function reverse :: [a] > [a]
reverses the order of elements in a list:
reverse "Hello" β "olleH"
Interestingly, we can express reverse
as a reduction as well. This may seem counterintuitive at first, considering the general specification of reverse
:
reverse [x_{1}, x_{2}, ... , x_{n}] β [x_{n}, ... , x_{2}, x_{1}]
which gives us a recursive pattern that doesn’t seem to fit a reduction:
reverse (x_{1} : (x_{2} : ... : (x_{n} : [])...) β (x_{n} : ... : (x_{2} : (x_{1} : [])...)
Here, we don’t replace (:)
and []
, but instead we replace the elements.
Let us take a step back. So far, we have always replaced cons (:)
by some existing standard function (such as (+)
or min
). How about replacing it by a function of our own design instead?
In other words, to implement reverse using the reduction pattern, we are looking for a function snoc
that we can use as follows:
reverse (x_{1} : (x_{2} : ... : (x_{n} : [])...) β
ββ(x_{1} `snoc` (x_{2} `snoc` ... `snoc` (x_{n} `snoc` [])...)
The function snoc
needs to have the same type signature as (:) :: a > [a] > [a]
; i.e.,
snoc :: a > [a] > [a]
Considering the first invocation of snoc in the call reverse "Hello"
(which is the same as reverse ('H':'e':'l':'l':'o':[])
), we know that we need
'H' `snoc` "olle" β "olleH"
The cons operator (:)
can only attach elements at the head
of a list and snoc
is essentially the reverse of that (hence, the name). To attach at the end, we need to use (++)
β i.e., we have got
x `snoc` xs = xs ++ [x]
and following the familiar pattern for reductions
reverse :: [a] > [a]
reverse [] = []
reverse (x:xs) = x `snoc` reverse xs
Given the simplicity of snoc
, we may like to inline its definition into reverse
, which gets us
reverse :: [a] > [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
This approach of using our own function to replace (:)
by way of the recursive pattern for reductions is a very powerful technique. In fact, any structurally recursive function can be implemented like that. This doesn’t always lead to an intuitive definition of a given function, but it is certainly possible.
Getting back to our definition of reverse
, the definition works, but it has a serious problem: from the definition of (++)
, we know that to append a single element [x]
at the end of a list, we have to recurse down the whole list until we reach []
, which we then replace by [x]
. reverse
has to do this in every single recursive step! That is quite computationally expensive.
π€’
Julia has a builtin package manager for installing addon functionality written in Julia. It can also install external libraries using your operating system’s standard system for doing so, or by compiling from source. The list of registered Julia packages can be found here. All package manager commands are found in the Pkg module
, included in Julia’s Base install
.
First we’ll go over the mechanics of the Pkg
family of commands and then we’ll provide some guidance on how to get your package registered. Be sure to read the section below on package naming conventions, tagging versions and the importance of a REQUIRE
file for when you’re ready to add your code to the curated METADATA
repository.
Package status
The Pkg.status()
function prints out a summary of the state of packages you have installed. Initially, you’ll have no packages installed:
Your package directory is automatically initialized the first time you run a Pkg
command that expects it to exist β which includes Pkg.status()
. Here’s an example nontrivial set of required and additional packages:
julia> Pkg.status()
Required packages:
 Distributions 0.2.8
 SHA 0.3.2
Additional packages:
 NumericExtensions 0.2.17
 Stats 0.2.6
These packages are all on registered versions, managed by Pkg
. Packages can be in more complicated states, indicated by annotations to the right of the installed package version; we will explain these states and annotations as we encounter them. For programmatic usage, Pkg.installed()
returns a dictionary, mapping installed package names to the version of that package which is installed:
Non ne ho, altrimenti otterrei qualcosa come questo
julia> Pkg.installed()
Dict{String,VersionNumber} with 4 entries:
"Distributions" => v"0.2.8"
"Stats" => v"0.2.6"
"SHA" => v"0.3.2"
"NumericExtensions" => v"0.2.17"
Aggiungere e rimuovere packages
Julia’s package manager is a little unusual in that it is declarative rather than imperative. This means that you tell it what you want and it figures out what versions to install (or remove) to satisfy those requirements optimally β and minimally. So rather than installing a package, you just add it to the list of requirements and then “resolve” what needs to be installed. In particular, this means that if some package had been installed because it was needed by a previous version of something you wanted, and a newer version doesn’t have that requirement anymore, updating will actually remove that package.
Your package requirements are in the file ~/.julia/v0.6/REQUIRE
. You can edit this file by hand and then call Pkg.resolve()
to install, upgrade or remove packages to optimally satisfy the requirements, or you can do Pkg.edit()
, which will open REQUIRE
in your editor (configured via the EDITOR
or VISUAL
environment variables), and then automatically call Pkg.resolve()
afterwards if necessary. If you only want to add or remove the requirement for a single package, you can also use the noninteractive Pkg.add()
and Pkg.rm()
commands, which add or remove a single requirement to REQUIRE
and then call Pkg.resolve()
.
You can add a package to the list of requirements with the Pkg.add()
function, and the package and all the packages that it depends on will be installed:
lista linghissima, finisce con
e uesta Γ¨ la directory ~/.julia/v0.6/
What this is doing is first adding Distributions to your ~/.julia/v0.6/REQUIRE
file:
It then runs Pkg.resolve()
using these new requirements, which leads to the conclusion that the Distributions
package should be installed since it is required but not installed. As stated before, you can accomplish the same thing by editing your ~/.julia/v0.6/REQUIRE
file by hand and then running Pkg.resolve()
yourself:
This is functionally equivalent to calling Pkg.add("SHA")
, except that Pkg.add()
doesn’t change REQUIRE
until after installation has completed, so if there are problems, REQUIRE
will be left as it was before calling Pkg.add()
. The format of the REQUIRE
file is described in Requirements Specification [prossimamente]; it allows, among other things, requiring specific ranges of versions of packages.
When you decide that you don’t want to have a package around any more, you can use Pkg.rm()
to remove the requirement for it from the REQUIRE
file:
Once again, this is equivalent to editing the REQUIRE
file to remove the line with each package name on it then running Pkg.resolve()
to update the set of installed packages to match. While Pkg.add()
and Pkg.rm()
are convenient for adding and removing requirements for a single package, when you want to add or remove multiple packages, you can call Pkg.edit()
to manually change the contents of REQUIRE
and then update your packages accordingly. Pkg.edit()
does not roll back the contents of REQUIRE
if Pkg.resolve()
fails β rather, you have to run Pkg.edit()
again to fix the files contents yourself.
Because the package manager uses libgit2
internally to manage the package git repositories, users may run into protocol issues (if behind a firewall, for example), when running Pkg.add()
. By default, all GitHubhosted packages wil be accessed via 'https'
; this default can be modified by calling Pkg.setprotocol!()
. The following command can be run from the command line in order to tell git to use 'https'
instead of the 'git'
protocol when cloning all repositories, wherever they are hosted:
git config global url."https://".insteadOf git://
However, this change will be systemwide and thus the use of Pkg.setprotocol!()
is preferable.
Note: The package manager functions also accept the .jl
suffix on package names, though the suffix is stripped internally. For example:
Pkg.add("Distributions.jl")
Pkg.rm("Distributions.jl")
π€’
Continuo da qui, copio qui, scrollare fino a “Recursion and Lists”.
Ricorsione e liste
We can not only use recursion to calculate numbers, but also to build lists: A simple example of such a recursive function is repeatN
, which produces a list that contains a given item n
times; i.e., has type
repeatN :: Int > a > [a]
To find a suitable definition for the function, we first consider what an appropriate base case might look like. Let us assume that we want the function to work for zero repetitions. Then, the expression repeatN 0 x
would have to return an empty list, which fully specifies the base case.
repN.hs
repeatN :: Int > a > [a]
repeatN 0 x = []
repeatN n x = x : repeatN (n  1) x
Prelude> :l repN
[1 of 1] Compiling Main ( repN.hs, interpreted )
Ok, 1 module loaded.
*Main> repeatN 0 "j"
[]
*Main> repeatN 8 "j"
["j","j","j","j","j","j","j","j"]
Given the material that we covered so far, it should be relatively straightforward to write a function that, when given a string, produces a list containing all of the possible suffixes of that string. For example, we would have
suffixes "Hello"ββ ["Hello", "ello", "llo", "lo", "o"]
The base case is when we have an empty string; then, we have no suffix, so we return the empty list:
suffixes "" = []
On the other hand, given
suffixes "ello" β ["ello", "llo", "lo", "o"]
we only need to add the string Hello
at the front of the result to get the value of suffixes Hello
. Moreover, as
tail Hello β ello
we arrive at the following definition:
suff.hs
suffixes :: String > [String]
suffixes "" = []
suffixes str = str : suffixes (tail str)
*Main> :l suff
[1 of 1] Compiling Main ( suff.hs, interpreted )
Ok, 1 module loaded.
*Main> suffixes "Hello"
["Hello","ello","llo","lo","o"]
In other words, after adding the current string str
, we only need the suffixes of tail str
.
Note that we can build lists recursively using only the empty list []
and the list forming operator (:
). As these two suffice to build any list, they are regarded as the basic list forming constructors. In fact, they are usually called nil and cons, respectively. (The word βnilβ actually stands for βNot In Listβ and βconsβ is an abbreviation for β(list) constructorβ. GiΓ sentite, chissΓ da dove vengono? uh! Lisp )
Liste come strutture ricorsive
All lists are constructed from nil and cons, where the following equality illustrates the correspondence between the square bracket and the nil/cons notation:
[x1, x2,... , xn] = (x1 : (x2 : ...: (xn : [])...)
Due to the repeated occurrence of cons, the right hand side exposes the recursive structure of lists. For each element xi in a list, we have one cons operator including this element into the list. Finally, each list is terminated by nil. This representation not only makes the recursive nature of lists explicit, but it is, in fact, the original representation of lists. The closed [x1, x2,... , xn]
notation is only a convenient shorthand.
Qui davvero devo richiamare l’attenzione sul mio logo
Pattern matching in liste
The nil and cons operators are so elementary that we not only use them to construct lists, but also to decompose them; much as we did with pattern matching to decompose tuples in this definition of fst
:
fst :: (a, b) > a
fst (x, y) = x
In a similar manner, we use pattern matching to decompose lists into their first element and the rest of the list; i.e., into the two components joined together by cons. In fact, this is exactly what the two functions head
and tail
do to extract the first element and the remaining elements from a list:
head :: [a] > a
head (x:xs) = x
tail :: [a] > [a]
tail (x:xs) = xs
In other words, they yield the two values that are used to compose a list using the cons operator. Thus, for every nonempty list xs
, we have the equality:
xs = head xs : tail xs
Therefore, the first component passed to cons is often called the head of the new list and the second component the tail.
In Fundamentals, we discussed that head
and tail
are partial functions as they lack a pattern matching the empty list. If we want to define a total function over lists with pattern matching, we have to specify at least two cases, one for the case where the input is an empty list and a second for the case where it is not empty; i.e., can be regarded as being constructed by a cons operator. The following function (also included in the Prelude
), which checks whether a given list is empty, covers both cases:
null :: [a] > Bool
null [] = True
null (x:xs) = False
*Main> null ""
True
*Main> null "Juhan"
False
Mappare: applicare un’operazione a ogni elemento di una lista
Combining pattern matching with recursion, we can traverse a list from beginning to end. Let’s say we have a list of numerals and want to compute the square of each element and return the resulting squared numbers as a new list:
allSquares [x1, x2,... , xn] = [x1 * x1, x2 * x2, ... , xn * xn]
For the base case, that is, empty list, we just return the empty list. If the list consists of a head x
and a tail xs
(pronounced: xes, as in boxes), we build a new list, with x * x
as head
, and the result of the recursive call allSquares xs
as tail
:
allSq.hs
allSquares :: Num a => [a] > [a]
allSquares [] = []
allSquares (x : xs) = x * x : allSquares xs
*Main> :l allSq
[1 of 1] Compiling Main ( allSq.hs, interpreted )
Ok, 1 module loaded.
*Main> allSquares [1, 2, 3, 4, 5]
[1,4,9,16,25]
With the same list traversal pattern, we can define a function allToUpper
which converts a string to upper case.
allToUpper "can you hear me now? β "CAN YOU HEAR ME NOW?"
To do so, we use a function defined in the standard module Data.Char
called toUpper :: Char > Char
, which converts a lower case letter to an uppercase letter and leaves all other characters as they are:
allUp.hs
import Data.Char
allToUpper :: String > String
allToUpper [] = []
allToUpper (chr : restString) = toUpper chr : allToUpper restString
*Main> :l allUp
[1 of 1] Compiling Main ( allUp.hs, interpreted )
Ok, 1 module loaded.
*Main> allToUpper "Juhan was here"
"JUHAN WAS HERE"
Per chi vuole di lΓ c’Γ¨ il video.
Apart from the names of the functions and variables, and that we have to import
the module Data.Char
, the functions allSquares
and allToUpper
are almost identical β both follow the pattern
recursiveFunction [] = []
recursiveFunction (x : xs) = doSomethingWith x : recursiveFunction xs
Such functions can get additional arguments than the list as parameter. For example, reusing the definition ColourPoint
from Fundamentals, we might define function that, given a point :: ColourPoint
together with a list of points points :: [ColourPoint]
, calculates the distance of each point in points
to point
:
distancesFromPoint :: ColourPoint > [ColourPoint] > [Float]
distancesFromPoint point [] = []
distancesFromPoint point (p : ps) = distance point p : distancesFromPoint point ps
This function still follows the same pattern of recursive list traversal as do allSquares
and allToUpper
.
π€’
Julia may be configured with a number of environment variables, either in the usual way of the operating system, or in a portable way from within Julia. Suppose you want to set the environment variable JULIA_EDITOR
to vim
, then either type ENV["JULIA_EDITOR"] = "vim"
for instance in the REPL to make this change on a case by case basis, or add the same to the user configuration file .juliarc.jl
in the user’s home directory to have a permanent effect. The current value of the same environment variable is determined by evaluating ENV["JULIA_EDITOR"]
.
The environment variables that Julia uses generally start with JULIA
. If Base.versioninfo
is called with verbose equal to true, then the output will list defined environment variables relevant for Julia, including those for which JULIA
appears in the name.
File locations
non so tradurlo
JULIA_HOME
The absolute path of the directory containing the Julia executable, which sets the global variable Base.JULIA_HOME
. If $JULIA_HOME
is not set, then Julia determines the value Base.JULIA_HOME
at runtime.
Nota per me: se si Γ¨ curiosi nella REPL si scrive eval("$JULIA_HOME")
.
The executable itself is one of
$JULIA_HOME/julia
$JULIA_HOME/juliadebug
by default.
The global variable Base.DATAROOTDIR
determines a relative path from Base.JULIA_HOME
to the data directory associated with Julia. Then the path $JULIA_HOME/$DATAROOTDIR/julia/base
determines the directory in which Julia initially searches for source files (via Base.find_source_file()
).
Likewise, the global variable Base.SYSCONFDIR
determines a relative path to the configuration file directory. Then Julia searches for a juliarc.jl
file at
$JULIA_HOME/$SYSCONFDIR/julia/juliarc.jl
$JULIA_HOME/../etc/julia/juliarc.jl
by default (via Base.load_juliarc()
).
For example, a Linux installation with a Julia executable located at /bin/julia
, a DATAROOTDIR
of ../share
, and a SYSCONFDIR
of ../etc
will have JULIA_HOME
set to /bin
, a sourcefile search path of /share/julia/base
and a global configuration search path of /etc/julia/juliarc.jl
.
Uh! mi sa che salto anche se ci sarebbero le variabili JULIA_LOAD_PATH
, JULIA_PKGDIR
, JULIA_HISTORY
e JULIA_PKGRESOLVE_ACCURACY
.
Applicazioni esterne
JULIA_SHELL
The absolute path of the shell with which Julia should execute external commands (via Base.repl_cmd()
). Defaults to the environment variable $SHELL
, and falls back to /bin/sh
if $SHELL
is unset, come da me.
Note: On Windows, this environment variable is ignored, and external commands are executed directly.
Salto le variabili che non m’interessano: JULIA_EDITOR
(non definito per me), JULIA_CPU_CORES
, JULIA_WORKER_TIMEOUT
, JULIA_NUM_THREADS
, JULIA_THREAD_SLEEP_THRESHOLD
e JULIA_EXCLUSIVE
.
Formattazione della REPL
Environment variables that determine how REPL output should be formatted at the terminal. Generally, these variables should be set to ANSI terminal escape sequences. Julia provides a highlevel interface with much of the same functionality: see the section on Interacting With Julia.
Ci sono JULIA_ERROR_COLOR
, JULIA_WARN_COLOR
, JULIA_INFO_COLOR
, JULIA_INPUT_COLOR
, JULIA_ANSWER_COLOR
, JULIA_STACKFRAME_LINEINFO_COLOR
e JULIA_STACKFRAME_FUNCTION_COLOR
, da me nessuna Γ¨ settata.
Mi lascio travolgere (Γ¨ lunedΓ¬) e ignoro debugging e profiling.
Il capitolo successivo Embedding Julia: As we have seen in Calling C and Fortran Code, Julia has a simple and efficient way to call functions written in C. But there are situations where the opposite is needed: calling Julia function from C code. This can be used to integrate Julia code into a larger C/C++ project, without the need to rewrite everything in C/C++. Julia has a C API to make this possible. As almost all programming languages have some way to call C functions, the Julia C API can also be used to build further language bridges (e.g. calling Julia from Python or C#).
Uh! salto nche questo. Troppo specialistico.
π€’
The functions that we considered so far only used a fixed number of elementary operations. Even
distance :: ColourPoint > ColourPoint > Float
distance (x1, y1, colour1) (x2, y2, colour2)
= sqrt (fromIntegral (dx * dx + dy * dy))
where
dx = x2  x1
dy = y2  y1
needs exactly one addition, two subtractions, two multiplications, the invocation of fromIntegral
, and one square root β which makes seven operations. If conditional expressions or guards are used, the number of operations may vary, but we can still place an upper limit on the number of operations (independently of the input passed to the function).
However, many problems cannot be solved by functions limited in this way; indeed, some functions βdepending on the inputβ have to perform an arbitrarily large number of operations. In the following, we look at a programming technique known as recursion as the fundamental mechanism to implementing such functions in Haskell.
RicorsivitΓ con numeri
Nota per me: affronta l’argomento dall’inizio dell’inizio ma si sa che sono niubbo
Consider the function natSum
, which computes the sum of all natural numbers up to a limit, or the Prelude
function product
, which computes the product of all of the elements of an integer list:
natSum n β n + (n1) + β― + 2 + 1 + 0
product [x1, x2,... , xn]ββ x1 * x2 * ... * xn
The above are not actual function definitions, since the notation β…β is not valid Haskell. However, they illustrate the meaning of the two functions in reasonably formal terms. From this specification of the meaning, we see that both functions require n
operations to compute their result. Thus, we can make two important observations:
It is this inputdependent repetition that we will implement by recursion
Calcolare n + ... + 2 + 1 + 0
Let us start with the simplest case: recursion over the natural numbers. How can we define the function natSum :: Num a => a > a
, which sums up all natural numbers from zero up to a given number n
? It should behave as natSum n = n + ... + 1 + 0
but how can we substitute the ellipsis by working program code?
To get an idea of what we would like to happen, consider the following rules describing the computations to be performed by natSum
for input values up to 5:
natSum 0 = 0
natSum 1 = 1 + 0
natSum 2 = 2 + 1 + 0
natSum 3 = 3 + 2 + 1 + 0
natSum 4 = 4 + 3 + 2 + 1 + 0
natSum 5 = 5 + 4 + 3 + 2 + 1 + 0
The above are legal Haskell definitions, but obviously, we would need an unbounded number of definitions to define natSum
for every possible input. There is, however, an observation that comes to our rescue: each of the above equations contains computations that already appear in the previous equations. For example, for natSum 5
, we have to evaluate 0 + 1 + 2 + 3 + 4 + 5
, but the subcomputation 0 + 1 + 2 + 3 + 4
already appears in natSum 4
. This seems like good news, as it allows us to reuse earlier equations for later ones. In other words, we could redefine natSum 5
as
natSum 5 = 5 + natSum 4
In fact, except for the first equation, we can systematically reuse the immediately preceding equation:
natSum 0 = 0
natSum 1 = 1 + natSum 0
natSum 2 = 2 + natSum 1
natSum 3 = 3 + natSum 2
natSum 4 = 4 + natSum 3
natSum 5 = 5 + natSum 4
...
Interestingly, all equations βexcept for the first oneβ now look exactly the same; they just use different values. This seems like an ideal situation to once more apply the trick that we already used when we introduced function definitions, i.e., we use abstraction to replace concrete values by variables and, in this way, we distill a repeating pattern out of the above equations. The repeating pattern is
natSum n = n + natSum m
where we know that m
always equals n  1
. In other words, given the two rules
natSum 0 = 0
natSum n = n + natSum (n  1)
we seem to have captured the essence of natSum
. In natural language, we could describe this essence as follows:
The sum of the natural numbers from 0
to 0
is 0
(first case). The sum of the natural numbers from 0
to n
can be obtained by computing the sum of the natural numbers from 0
to nβ
ββ
1
, and then, adding n
(second case).
This sounds sensible; indeed, it is sufficient to compute the result of natSum
for every case. For example, let us look at the stepwise evaluation of one application of natSum
:
natSum 5 βββ5 + natSum (5  1)
βββββββ ββ5 + natSum 4
βββββββ ββ5 + (4 + natSum (4  1))
βββββββ ββ5 + (4 + natSum 3)
βββββββ ββ5 + (4 + (3 + natSum (3  1)))
βββββββ ββ5 + (4 + (3 + natSum 2))
βββββββ ββ5 + (4 + (3 + (2 + natSum (2  1))))
βββββββ ββ5 + (4 + (3 + (2 + natSum 1)))
βββββββ ββ5 + (4 + (3 + (2 + (1 + natSum (1  1)))))
βββββββ ββ5 + (4 + (3 + (2 + (1 + natSum 0))))
βββββββ ββ5 + (4 + (3 + (2 + (1 + 0))))
βββββββ ββ15
This obviously works the way we intended it to work. The above definition of natSum
is called recursive, because natSum
itself is used in the definition of natSum
β i.e., a recursive function is a function that makes use of itself in its definition.
Recursive function definitions have at least two cases: the base case and the recursive case (or stepping case). The base case specifies what to do in the simplest form of input (where the function stops calling itself), whereas the stepping case includes the recursive use of the function by itself:
natSum :: Num a => a > a
natSum 0 = 0  base case
natSum n = n + natSum (n  1)  recursive/stepping case
Later, we will encounter recursive functions with more than one base case and/or more than one recursive case β but there will always be at least one of each kind.
An alternative way of writing the recursive definition of natSum
would be
natSum :: Num a => a > a
natSum n = if n == 0
then 0
else n + natSum (n  1)
It contains only one equation and makes the case distinction explicit through a conditional.
A question that might come up during the definition of natSum
is, what happens if we call this function with an argument that causes the recursive case to move away from, rather than towards the base case. For example, what does natSum (1)
result in? As the recursive case is applicable, the argument will be reduced to 2
, 3
, and so on, which means that we enter an infinite computation. In other words, natSum
is not defined for arguments smaller than 0
.
This is another instance of the concept of partial functions, which we discussed here. However, here the problem is not simply a lack of an undefined input pattern, but that the function fails to terminate for some of the input values admitted by its type signature. To replace nontermination by a proper runtime error, we can use the previously discussed error function:
natSum :: (Num a, Ord a) => a > a
natSum 0 = 0
natSum n  n > 0 = n + natSum (n  1)
 otherwise = error "natSum: Input value too small!"
Volendo di lΓ c’Γ¨ il video.
π€’
When dealing with platform libraries, it is often necessary to provide special cases for various platforms. The variable Sys.KERNEL
can be used to write these special cases. There are several functions intended to make this easier: is_unix
, is_linux
, is_apple
, is_bsd
, and is_windows
.
These may be used as follows:
if is_windows()
some_complicated_thing(a)
end
Note that is_linux
and is_apple
are mutually exclusive subsets of is_unix
. Additionally, there is a macro @static
which makes it possible to use these functions to conditionally hide invalid code, as demonstrated in the following examples.
Simple blocks:
ccall( (@static is_windows() ? :_fopen : :fopen), ...)
Complex blocks:
@static if is_linux()
some_complicated_thing(a)
else
some_different_thing(a)
end
When chaining conditionals (including if/elseif/end
), the @static
must be repeated for each level (parentheses optional, but recommended for readability):
@static is_windows() ? :a : (@static is_apple() ? :b : :c)
π€’
Continuo da qui, copio qui, scrollare fino a “Exercises”.
Resta da fare un ultimo esercizio, ne approfitto per qualche considerazione personale.
5. Implement division on Int
, divide :: Int > Int > Int
using the list functions described in this section. Hint: first, write a function that returns all the multiples of a given number up to a specific limit.
divide 5 10 ββ2
divide 5 8 ββ1
divide 3 10ββ 3
Non sono ancora abituato a Haskell, ho iniziato trascurando il suggerimento, cosΓ¬:
Prelude> 10 / 3
3.3333333333333335
Prelude> round 10 / 3
:2:1: error:
β’ Ambiguous type variable βa0β arising from a use of βprintβ
prevents the constraint β(Show a0)β from being solved.
Probable fix: use a type annotation to specify what βa0β should be.
These potential instances exist:
instance Show Ordering  Defined in βGHC.Showβ
instance Show Integer  Defined in βGHC.Showβ
instance Show a => Show (Maybe a)  Defined in βGHC.Showβ
...plus 22 others
...plus 12 instances involving outofscope types
(use fprintpotentialinstances to see them all)
β’ In a stmt of an interactive GHCi command: print it
Prelude> round (10 / 3)
3
OK, le parentesi. Provo a mettere tutto nel file
dr.hs
dr :: Int > Int > Int
dr x y = round (x / y)
Prelude> :l dr
[1 of 1] Compiling Main ( dr.hs, interpreted )
dr.hs:3:10: error:
β’ No instance for (RealFrac Int) arising from a use of βroundβ
β’ In the expression: round (x / y)
In an equation for βdrβ: dr x y = round (x / y)

3  dr x y = round (x / y)
 ^^^^^^^^^^^^^
dr.hs:3:17: error:
β’ No instance for (Fractional Int) arising from a use of β/β
β’ In the first argument of βroundβ, namely β(x / y)β
In the expression: round (x / y)
In an equation for βdrβ: dr x y = round (x / y)

3  dr x y = round (x / y)
 ^^^^^
Failed, 0 modules loaded.
No, ovvio che /
non da Int
, provo a eliminare la signature
drn.hs
dr x y = round (x / y)
Prelude> :l drn
[1 of 1] Compiling Main ( drn.hs, interpreted )
Ok, 1 module loaded.
*Main> dr 10 3
3
OK, anche se non Γ¨ quello richiesto e poi capita questo
*Main> dr 8 3
3
Si potrebbe cambiare round
con floor
e i conti tornerebbero. Anche se non Γ¨ solo per Int
.
Insomma, anche se spero ci sia un metodo piΓΉ semplice do retta ai prof, ecco –dopo diversi tentativi, l’ho detto che sono niubbo:
Prelude> [3, 6..10]
[3,6,9]
Prelude> x=3
Prelude> y = 10
Prelude> [x, (2*x)..y]
[3,6,9]
Prelude> length [x, (2*x)..y]
3
Posso quindi costruire lo script
div.hs
divide :: Int > Int > Int
divide x y = length [x, (2*x)..y]
*Main> :l div
[1 of 1] Compiling Main ( div.hs, interpreted )
Ok, 1 module loaded.
*Main> divide 5 10
2
*Main> divide 5 8
1
*Main> divide 3 10
3
Fatto! era semplice, semplicissimissimo, bastava seguire quanto detto a lezione (cioΓ¨ nei posts precedenti). Solo che –tentativo di giustificazione qui– da solo Γ¨ piΓΉ difficile, rischi di innamorarti acriticamente delle tue idee. E poi sono condizionato da altri linguaggi. Ma poi mi passa … probabilmente … forse
π€’
Exercise 2.62: Give a Ξ(n)
implementation of unionset
for sets represented as ordered lists.
Uh! Aiuto Bill the Lizard
Since the two sets are in order, we can simply step through each set comparing the first elements of each. At each step we decide which of the first two elements to place in the resulting set.
(define (unionset set1 set2)
(cond ((null? set1) set2)
((null? set2) set1)
((= (car set1) (car set2))
(cons (car set1) (unionset (cdr set1) (cdr set2))))
((< (car set1) (car set2))
(cons (car set1) (unionset (cdr set1) set2)))
(else (cons (car set2) (unionset set1 (cdr set2))))))
Anyone familiar with the mergesort algorithm will quickly recognize that this implementation of unionset
is almost exactly the same procedure as the merge subroutine. It’s purpose is to quickly merge two sorted lists into one. The only difference is that the unionset
implementation above only keeps one copy of duplicate elements.
sicpex ha diverse soluzioni, Drewiki una sola ma OK
π€’
Specifiche per funzioni non costanti
A (name, library)
function specification must be a constant expression. However, it is possible to use computed values as function names by staging through eval
as follows:
@eval ccall(($(string("a", "b")), "lib"), ...
This expression constructs a name using string, then substitutes this name into a new ccall
expression, which is then evaluated. Keep in mind that eval
only operates at the top level, so within this expression local variables will not be available (unless their values are substituted with $
). For this reason, eval
is typically only used to form toplevel definitions, for example when wrapping libraries that contain many similar functions.
If your usage is more dynamic, use indirect calls as described in the next section.
Chiamate indirette
The first argument to ccall
can also be an expression evaluated at run time. In this case, the expression must evaluate to a Ptr
, which will be used as the address of the native function to call. This behavior occurs when the first ccall
argument contains references to nonconstants, such as local variables, function arguments, or nonconstant globals.
For example, you might look up the function via dlsym
, then cache it in a global variable for that session. For example:
macro dlsym(func, lib)
z, zlocal = gensym(string(func)), gensym()
eval(current_module(), :(global $z = C_NULL))
z = esc(z)
quote
let $zlocal::Ptr{Void} = $z::Ptr{Void}
if $zlocal == C_NULL
$zlocal = dlsym($(esc(lib))::Ptr{Void}, $(esc(func)))
global $z = $zlocal
end
$zlocal
end
end
end
Convenzioni di chiamata
The second argument to ccall
can optionally be a calling convention specifier (immediately preceding return type). Without any specifier, the platformdefault C calling convention is used. Other supported conventions are: stdcall
, cdecl
, fastcall
, and thiscall
. For example (from base/libc.jl
) we see the same gethostnameccall
as above, but with the correct signature for Windows:
hn = Vector{UInt8}(256)
err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn))
There is one additional special calling convention llvmcall
, which allows inserting calls to LLVM intrinsics directly. This can be especially useful when targeting unusual platforms such as GPGPUs. For example, for CUDA, we need to be able to read the thread index:
ccall("llvm.nvvm.read.ptx.sreg.tid.x", llvmcall, Int32, ())
As with any ccall
, it is essential to get the argument signature exactly correct. Also, note that there is no compatibility layer that ensures the intrinsic makes sense and works on the current target, unlike the equivalent Julia functions exposed by Core.Intrinsics
.
Accedere a variabili globali
Global variables exported by native libraries can be accessed by name using the cglobal()
function. The arguments to cglobal()
are a symbol specification identical to that used by ccall
, and a type describing the value stored in the variable:
julia> cglobal((:errno, :libc), Int32)
Ptr{Int32} @0x00007f418d0816b8
The result is a pointer giving the address of the value. The value can be manipulated through this pointer using unsafe_load()
and unsafe_store!()
.
Accedere a dati tramite pointers
The following methods are described as “unsafe” because a bad pointer or type declaration can cause Julia to terminate abruptly.
Given a Ptr{T}
, the contents of type T
can generally be copied from the referenced memory into a Julia object using unsafe_load(ptr, [index])
. The index
argument is optional (default is 1), and follows the Juliaconvention of 1based indexing. This function is intentionally similar to the behavior of getindex()
and setindex!()
(e.g. []
access syntax).
The return value will be a new object initialized to contain a copy of the contents of the referenced memory. The referenced memory can safely be freed or released.
If T
is Any
, then the memory is assumed to contain a reference to a Julia object (a jl_value_t*
), the result will be a reference to this object, and the object will not be copied. You must be careful in this case to ensure that the object was always visible to the garbage collector (pointers do not count, but the new reference does) to ensure the memory is not prematurely freed. Note that if the object was not originally allocated by Julia, the new object will never be finalized by Julia’s garbage collector. If the Ptr
itself is actually a jl_value_t*
, it can be converted back to a Julia object reference by unsafe_pointer_to_objref(ptr)
. (Julia values v can be converted to jl_value_t*
pointers, as Ptr{Void}
, by calling pointer_from_objref(v)
.)
The reverse operation (writing data to a Ptr{T}
), can be performed using unsafe_store!(ptr, value, [index])
. Currently, this is only supported for primitive types or other pointerfree (isbits
) immutable struct types.
Any operation that throws an error is probably currently unimplemented and should be posted as a bug so that it can be resolved.
If the pointer of interest is a plaindata array (primitive type or immutable struct), the function unsafe_wrap(Array, ptr,dims,[own])
may be more useful. The final parameter should be true if Julia should “take ownership” of the underlying buffer and call free(ptr)
when the returned Array
object is finalized. If the own parameter is omitted or false, the caller must ensure the buffer remains in existence until all access is complete.
Arithmetic on the Ptr
type in Julia (e.g. using +
) does not behave the same as C’s pointer arithmetic. Adding an integer to a Ptr
in Julia always moves the pointer by some number of bytes, not elements. This way, the address values obtained from pointer arithmetic do not depend on the element types of pointers.
Threadsafety
Some C libraries execute their callbacks from a different thread, and since Julia isn’t threadsafe you’ll need to take some extra precautions. In particular, you’ll need to set up a twolayered system: the C callback should only schedule (via Julia’s event loop) the execution of your “real” callback. To do this, create a AsyncCondition
object and wait on it:
cond = Base.AsyncCondition()
wait(cond)
The callback you pass to C should only execute a ccall
to :uv_async_send
, passing cond.handle
as the argument, taking care to avoid any allocations or other interactions with the Julia runtime.
Note that events may be coalesced, so multiple calls to uv_async_send
may result in a single wakeup notification to the condition.
Maggiori informazioni sulle callabcks
For more details on how to pass callbacks to C libraries, see this blog post.
C++
For direct C++ interfacing, see the Cxx
package. For tools to create C++ bindings, see the CxxWrap package.
Fine del capitolo. Con la speranza che di non doverlo usare mai.
π€’