Category Archives: Linguaggi

Linguaggi di programmazione

Haskell – 16 – ricorsione – 2

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 non-empty 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, re-using 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.

🤢

Annunci

Julia – 99 – variabili d’ambiente

Continuo da qui, copio qui.

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 run-time.

Nota per me: se si è curiosi nella REPL si scrive eval("$JULIA_HOME").

The executable itself is one of

$JULIA_HOME/julia
$JULIA_HOME/julia-debug

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 source-file 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 high-level 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.

🤢

Haskell – 15 – ricorsione – 1

Continuo da qui, copio qui.

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 + (n-1) + ⋯ + 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:

  • The number of operations depends on the input.
  • A certain computation (or more generally, a set of operations) is repeatedly used.

It is this input-dependent 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 non-termination 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.

🤢

Julia – 98 – gestire varianti legate al sistema operativo

Continuo da qui, copio qui.

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)

🤢

Haskell – 14 – elementi fondamentali – 8

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 out-of-scope types
        (use -fprint-potential-instances 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 😊

🤢

SICP – cap. 2 – Esempio: sets come liste ordinate – 82 – esercizio

Continuo da qui, copio qui.

Exercise 2.62: Give a Θ(n) implementation of union-set 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 (union-set set1 set2)
  (cond ((null? set1) set2)
        ((null? set2) set1)
        ((= (car set1) (car set2))
         (cons (car set1) (union-set (cdr set1) (cdr set2))))
        ((< (car set1) (car set2))
         (cons (car set1) (union-set (cdr set1) set2)))
        (else (cons (car set2) (union-set set1 (cdr set2))))))

Anyone familiar with the mergesort algorithm will quickly recognize that this implementation of union-set 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 union-set implementation above only keeps one copy of duplicate elements.

sicp-ex ha diverse soluzioni, Drewiki una sola ma OK 😊

🤢

Julia – 97 – Eseguire codice C e Fortran – 7

Continuo da qui, copio qui.

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 top-level 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 non-constants, such as local variables, function arguments, or non-constant 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 platform-default 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 Julia-convention of 1-based 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 pointer-free (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 plain-data 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.

Thread-safety
Some C libraries execute their callbacks from a different thread, and since Julia isn’t thread-safe you’ll need to take some extra precautions. In particular, you’ll need to set up a two-layered 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.

🤢

Haskell – 13 – elementi fondamentali – 7

Continuo da qui, copio qui, scrollare fino a “Exercises”.

3. Define a function isLower :: Char -> Bool which returns True if a given character is a lower case letter. You can use the fact that characters are ordered, and for all lower case letters ch we have ′a′ ≤ ch and ch ≤ ′z′. Alternatively, you can use the fact that ['a'..'z'] evaluates to a list containing all lower case letters.

isl-1.hs

isLower :: Char -> Bool

isLower x = 'a' <= x && x <= 'z'
Prelude> :l isl-1
[1 of 1] Compiling Main             ( isl-1.hs, interpreted )
Ok, 1 module loaded.
*Main> isLower 'j'
True
*Main> isLower 'a'
True
*Main> isLower 'z'
True
*Main> isLower '0'
False
*Main> isLower 'T'
False

Io la modificherei in questo modo (sapete, vengo da lontano (Fortran)😊)

isl-2.hs

isLower :: Char -> Bool

isLower x = x >= 'a' && x <= 'z'
*Main> :l isl-2
[1 of 1] Compiling Main             ( isl-2.hs, interpreted )
Ok, 1 module loaded.
*Main> isLower 'j'
True
*Main> isLower 'T'
False
*Main> isLower '.'
False
*Main> isLower '~'
False

Ma ancora più sexy questa versione

isl-3.hs

isLower :: Char -> Bool

isLower x = elem x ['a'..'z']
*Main> :l isl-3
[1 of 1] Compiling Main             ( isl-3.hs, interpreted )
Ok, 1 module loaded.
*Main> isLower 'l'
True
*Main> isLower 'U'
False

4. Write a function mangle :: String -> String which removes the first letter of a word and attaches it at the end. If the string is empty, mangle should simply return an empty string:

mangle "Hello" ⇒ "elloH"
mangle "I" ⇒ "I"
mangle "" ⇒  ""

Non ci sto riuscendo. Uh! capito 😁, dopo diversi tentativi (che non racconto, già dimenticati)

mgl.hs

mangle :: String -> String

mangle x = if (length x <= 1) then x else tail x ++ [head x]
*Main> :l mgl
[1 of 1] Compiling Main             ( mgl.hs, interpreted )
Ok, 1 module loaded.
*Main> mangle "Hello"
"elloH"
*Main> mangle "I"
"I"
*Main> mangle ""
""

Personalmente preferisco scriverlo con le  guardie  guards:

mgl-g

mangle :: String -> String

mangle x | (length x <= 1) = x 
         | otherwise = tail x ++ [head x]

Resta da fare il 5. Prossimamente, forse 😊

🤢

Julia – 96 – Eseguire codice C e Fortran – 6

Continuo da qui, copio qui.

Qualche esempi ci wrappers C
Here is a simple example of a C wrapper that returns a Ptr type:

mutable struct gsl_permutation
end

# The corresponding C signature is
#     gsl_permutation * gsl_permutation_alloc (size_t n);
function permutation_alloc(n::Integer)
    output_ptr = ccall(
        (:gsl_permutation_alloc, :libgsl), # name of C function and library
        Ptr{gsl_permutation},              # output type
        (Csize_t,),                        # tuple of input types
        n                                  # name of Julia variable to pass in
    )
    if output_ptr == C_NULL # Could not allocate memory
        throw(OutOfMemoryError())
    end
    return output_ptr
end

The GNU Scientific Library (here assumed to be accessible through :libgsl) defines an opaque pointer, gsl_permutation *, as the return type of the C function gsl_permutation_alloc(). As user code never has to look inside the gsl_permutation struct, the corresponding Julia wrapper simply needs a new type declaration, gsl_permutation, that has no internal fields and whose sole purpose is to be placed in the type parameter of a Ptr type. The return type of the ccall is declared as Ptr{gsl_permutation}, since the memory allocated and pointed to by output_ptr is controlled by C (and not Julia).

The input n is passed by value, and so the function’s input signature is simply declared as (Csize_t,) without any Ref or Ptr necessary. (If the wrapper was calling a Fortran function instead, the corresponding function input signature should instead be (Ref{Csize_t},), since Fortran variables are passed by reference.) Furthermore, n can be any type that is convertable to a Csize_t integer; the ccall implicitly calls Base.cconvert(Csize_t, n).

Here is a second example wrapping the corresponding destructor:

# The corresponding C signature is
#     void gsl_permutation_free (gsl_permutation * p);
function permutation_free(p::Ref{gsl_permutation})
    ccall(
        (:gsl_permutation_free, :libgsl), # name of C function and library
        Void,                             # output type
        (Ref{gsl_permutation},),          # tuple of input types
        p                                 # name of Julia variable to pass in
    )
end

Here, the input p is declared to be of type Ref{gsl_permutation}, meaning that the memory that p points to may be managed by Julia or by C. A pointer to memory allocated by C should be of type Ptr{gsl_permutation}, but it is convertable using Base.cconvert() and therefore can be used in the same (covariant) context of the input argument to a ccall. A pointer to memory allocated by Julia must be of type Ref{gsl_permutation}, to ensure that the memory address pointed to is valid and that Julia’s garbage collector manages the chunk of memory pointed to correctly. Therefore, the Ref{gsl_permutation} declaration allows pointers managed by C or Julia to be used.

If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using p::Ptr{gsl_permutation} for the method signature of the wrapper and similarly in the ccall is also acceptable.

Here is a third example passing Julia arrays:

# The corresponding C signature is
#    int gsl_sf_bessel_Jn_array (int nmin, int nmax, double x,
#                                double result_array[])
function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real)
    if nmax < nmin
        throw(DomainError())
    end
    result_array = Vector{Cdouble}(nmax - nmin + 1)
    errorcode = ccall(
        (:gsl_sf_bessel_Jn_array, :libgsl), # name of C function and library
        Cint,                               # output type
        (Cint, Cint, Cdouble, Ref{Cdouble}),# tuple of input types
        nmin, nmax, x, result_array         # names of Julia variables to pass in
    )
    if errorcode != 0
        error("GSL error code $errorcode")
    end
    return result_array
end

The C function wrapped returns an integer error code; the results of the actual evaluation of the Bessel J function populate the Julia array result_array. This variable can only be used with corresponding input type declaration Ref{Cdouble}, since its memory is allocated and managed by Julia, not C. The implicit call to Base.cconvert(Ref{Cdouble}, result_array) unpacks the Julia pointer to a Julia array data structure into a form understandable by C.

Note that for this code to work correctly, result_array must be declared to be of type Ref{Cdouble} and not Ptr{Cdouble}. The memory is managed by Julia and the Ref signature alerts Julia’s garbage collector to keep managing the memory for result_array while the ccall executes. If Ptr{Cdouble} were used instead, the ccall may still work, but Julia’s garbage collector would not be aware that the memory declared for result_array is being used by the external C function. As a result, the code may produce a memory leak if result_array never gets freed by the garbage collector, or if the garbage collector prematurely frees result_array, the C function may end up throwing an invalid memory access exception.

Garbage collection in modo sicuro
When passing data to a ccall, it is best to avoid using the pointer() function. Instead define a convert method and pass the variables directly to the ccall. ccall automatically arranges that all of its arguments will be preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the ccall returns, you must arrange that the object remains visible to the garbage collector. The suggested way to handle this is to make a global variable of type Array{Ref,1} to hold these values, until the C library notifies you that it is finished with them.

🤢

Haskell – 12 – elementi fondamentali – 6

Continuo da qui, copio qui, scrollare fino a “Exercises”.

Esercizi
1. Write a function sort2 :: Ord a => a -> a -> (a, a) which accepts two Int values as arguments and returns them as a sorted pair, so that sort2 5 3 is equal to (3,5). How can you define the function using a conditional, how can you do it using guards?

es1-1.hs

sort2 :: Ord a => a -> a -> (a, a)
sort2 x y = if x >= y then (y, x) else (x, y)
Prelude> :l es1-1
[1 of 1] Compiling Main             ( es1-1.hs, interpreted )
Ok, 1 module loaded.
*Main> sort2 5 3
(3,5)
*Main> sort2 1 8
(1,8)

es1-2.hs

sort2 :: Ord a => a -> a -> (a, a)
sort2 x y | x >= y = (y, x) 
          | x < y  = (x, y)
*Main> :l es1-2
[1 of 1] Compiling Main             ( es1-2.hs, interpreted )
Ok, 1 module loaded.
*Main> sort2 5 3
(3,5)
*Main> sort2 1 8
(1,8)

2. Consider a function

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

which compares the values of two pairs. It returns True if both pairs contain the same values, regardless of the order. For example, almostEqual (3,4) (4,3) is True, but almostEqual (3,4) (3,5) is False. Which of the following definitions return the correct value? Which of the definitions would you consider good style? Why?

(The operator (&&) is logical ”and”, the operator (||) is logical ’or’, and (==) tests if two values are equal. The first two are of type Bool -> Bool -> Bool; the third is of type Eq a => a -> a -> Bool.)

almostEqual (x1, y1) (x2, y2)
  | (x1 == x2) && (y1 == y2) = True
  | (x1 == y2) && (y1 == x2) = True
  | otherwise                = False

almostEqual (x1, y1) (x2, y2)
  | (x1 == x2) = (y1 == y2) 
  | (x1 == y2) = (y1 == x2) 
  | otherwise  = False

almostEqual pair1 pair2 
  = (pair1 == pair2) || (swap pair1 == pair2)
  where 
    swap (x,y) = (y,x)

almostEqual pair1 pair2 
  = (pair1 == pair2) || (swap pair1 == swap pair2)
  where 
    swap (x,y) = (y,x)

almostEqual (x1, y1) (x2, y2) 
  = if (x1 == x2) 
      then
        if (y1 == y2) 
          then True
          else False
      else 
        if (x1 == y2) 
          then 
            if (x2 == y1)
              then True
              else False
          else False

La prima versione (es2-a.hs) è OK

es2-a.hs

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

almostEqual (x1, y1) (x2, y2)
  | (x1 == x2) && (y1 == y2) = True
  | (x1 == y2) && (y1 == x2) = True
  | otherwise                = False
*Main> :l es2-a
[1 of 1] Compiling Main             ( es2-a.hs, interpreted )
Ok, 1 module loaded.
*Main> almostEqual (3,4) (4,3)
True
*Main> almostEqual (3,4) (3,5)
False

La seconda (es2-b.hs) anche.

es2-b.hs

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

almostEqual (x1, y1) (x2, y2)
  | (x1 == x2) = (y1 == y2)
  | (x1 == y2) = (y1 == x2)
  | otherwise  = False
Prelude> :l es2-b
[1 of 1] Compiling Main             ( es2-b.hs, interpreted )
Ok, 1 module loaded.
*Main> almostEqual (3,4) (4,3)
True
*Main> almostEqual (3,4) (3,5)
False

perché ritorna il valore dato dall’operatore = se questo è True; se False ritorna la guard finale (otherwise). La prima è (forse) più chiara, la seconda (credo) più haskelliana. Anzi, ripensandoci la seconda è molto più bella.

La terza (es2-c.hs) è ancora più haskelliana, deriva dalla seconda:

es2-c.hs

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

almostEqual pair1 pair2
  = (pair1 == pair2) || (swap pair1 == pair2)
  where 
    swap (x,y) = (y,x)
*Main> :l es2-c
[1 of 1] Compiling Main             ( es2-c.hs, interpreted )
Ok, 1 module loaded.
*Main> almostEqual (3,4) (4,3)
True
*Main> almostEqual (3,4) (3,5)
False

la quarta (es2-d.hs) non è corretta per lo swap di entrambe le pair in quanto devo verificare se una è il rovescio dell’altra.

es2-d.hs

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

almostEqual pair1 pair2
  = (pair1 == pair2) || (swap pair1 == swap pair2)
  where 
    swap (x,y) = (y,x)

risultati non corretti

*Main> :l es2-d
[1 of 1] Compiling Main             ( es2-d.hs, interpreted )
Ok, 1 module loaded.
*Main> almostEqual (3,4) (4,3)
False
-- risultato NON corretto!!!
*Main> almostEqual (3,4) (3,5)
False
*Main> almostEqual (3,4) (3,4)
True

la quinta (es2-e.hs) è OK ma primitiva, sembra Python (o peggio)

es2-e.hs

almostEqual :: Eq a => (a, a) -> (a, a) -> Bool

almostEqual (x1, y1) (x2, y2) 
  = if (x1 == x2) 
      then
        if (y1 == y2) 
          then True
          else False
      else 
        if (x1 == y2) 
          then 
            if (x2 == y1)
              then True
              else False
          else False
*Main> :l es2-e
[1 of 1] Compiling Main             ( es2-e.hs, interpreted )
Ok, 1 module loaded.
*Main> almostEqual (3,4) (4,3)
True
*Main> almostEqual (3,4) (3,5)
False

Ci sono ancora 3 esercizi, per il prossimo post 😊

🤢