Prop A Cheat Sheet
Prop A Cheat Sheet
Funktionale Programmierung
Funktionale Programmierung
Begriffe
Haskell Prelude
Basic data types
Basic type classes
Numeric types
Numeric type classes
Numeric functions
Monads and functors
Folds and traversals
Miscellaneous functions
List operations
Special folds
Building lists
Sublists
Searching lists
Zipping and unzipping
Functions on strings
base
module Control.Applicative where
module Control.Monad where
module Data.Bifunctor where
module Data.Char where
module Data.Function where
module Data.List where
module Data.Ord where
module Data.Tuple where
module Numeric where
MissingH
module Data.List.Utils where
Begriffe Haskell Prelude
Eine Funktion heißt linear rekursiv, wenn in jedem Definitionszweig nur ein rekursiver Aufruf https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelude.html
vorkommt. (https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelude.html)
Eine linear rekursive Funktion heißt endrekursiv (tail recursive), wenn in jedem Zweig der Basic data types
rekursive Aufruf nicht in andere Aufrufe eingebettet ist.
-- endrekursiv mit Akkumulator, O(n) rekursive Aufrufe (&&) :: Bool -> Bool -> Bool Boolean “and”, lazy in the second
fib n = fibAcc n 0 1 ( infixr 3 ) argument
where
fibAcc n n1 n2 (||) :: Bool -> Bool -> Bool
Boolean “or”, lazy in the second argument
| (n == 0) = n1 ( infixr 2 )
| (n == 1) = n2 not :: Bool -> Bool Boolean “not”
| otherwise = fibAcc (n - 1) n2 (n1 + n2)
otherwise is defined as the value True .
otherwise :: Bool
It helps to make guards more readable.
Funktionen, die andere Funktionen als Parameter erhalten oder Funktionen als
Rückgabewerte liefern, heißen Funktionen höherer Ordnung.
Currying ist die Ersetzung einer mehrstelligen Funktion durch Schachtelung einstelliger data Maybe a = Nothing | Just a
Funktionen. Funktionen in Haskell sind gecurryt. Funktionsanwendung ist linksassoziativ.
The Maybe type encapsulates an optional value. A value of type Maybe a either contains a
Extensionalitätsprinzip: f = g ⟺ ∀x ∈ A : f (x) = g(x) für alle f , g : A → B . value of type a (represented as Just a ), or it is empty (represented as Nothing ). Using
Maybe is a good way to deal with errors or exceptional cases without resorting to drastic
Bindungskonstrukte legen Bedeutung und Geltungsbereich von Variablen fest, z. B.
measures such as error .
Funktionsdefinitionen und λ-Abstraktionen. Innere Bindungen verdecken äußere.
The Maybe type is also a monad. It is a simple kind of error monad, where all errors are
Referenzielle Transparenz: Im gleichen Gültigkeitsbereich bedeuten gleiche Ausdrücke stets
represented by Nothing . A richer error monad can be built using the Either type.
das Gleiche. Zwei verschiedene Ausdrücke, die zum gleichen Wert auswerten, können stets
durch den anderen ersetzt werden, ohne die Bedeutung des Programms zu verändern. Instances: Functor , Monad , Read , Show , Eq , Ord
Fehlerhafte Auswertungen (⊥):
Signature Docs
Nichttermination, z. B. fib (-1) (s. o.)
The maybe function takes a default value,
Laufzeitfehler, z. B. div 1 o a function, and a Maybe value. If the
maybe :: b -> (a -> b) -> Maybe a -> Maybe value is Nothing , the function
b returns the default value. Otherwise, it
applies the function to the value inside the
Just and returns the result.
Basic type classes
data Either a b = Left a | Right b
The Either type represents values with two possibilities: a value of type Either a b is class Eq a where
either Left a or Right b .
The Eq class defines equality ( == ) and inequality ( /= ). All the basic datatypes exported
The Either type is sometimes used to represent a value which is either correct or an error;
by the Prelude are instances of Eq , and Eq may be derived for any datatype whose
by convention, the Left constructor is used to hold an error value and the Right
constituents are also instances of Eq .
constructor is used to hold a correct value (mnemonic: “right” also means “correct”).
The Haskell Report defines no laws for Eq . However, instances are encouraged to follow
Instances: Bifunctor , Foldable , Applicative , Functor , Monad , Read , Show , Eq ,
these properties:
Ord
Reflexivity: x == x = True
Signature Docs Symmetry: x == y = y == x
Case analysis for the Either type. If the Transitivity: if x == y && y == z = True , then x == z = True
either :: (a -> c) -> (b -> c) -> value is Left a , apply the first function Extensionality: if x == y = True and f is a function whose return type is an instance
Either a b -> c to a ; if it is Right b , apply the second of Eq , then f x == f y = True
function to b . Negation: x /= y = not (x == y)
snd :: (a, b) -> b Extract the second component of a pair. Comparability: x <= y || y <= x = True
Transitivity: if x <= y && y <= z = True , then x <= z = True
curry :: ((a, b) -> c) -> a -> b -> curry converts an uncurried function to
c a curried function. Reflexivity: x <= x = True
Antisymmetry: if x <= y && y <= x = True , then x == y = True
uncurry :: (a -> b -> c) -> (a, b) - uncurry converts a curried function to a
> c function on pairs.
The following operator interactions are expected to hold: x >= y = y <= x , x < y = x <= Signature Docs
y && x /= y , x > y = y < x , x < y = compare x y == LT , x > y = compare x y ==
GT , x == y = compare x y == EQ , min x y == if x <= y then x else y = True , max x the successor of a value. For numeric
succ :: a -> a
y == if x >= y then x else y = True types, succ adds 1.
Minimal complete definition: compare | (<=) the predecessor of a value. For numeric
pred :: a -> a
types, pred subtracts 1.
Signature Docs toEnum :: Int -> a Convert from an Int .
compare :: a -> a -> Ordering Convert to an Int . It is implementation-
(<) :: a -> a -> Bool dependent what fromEnum returns when
fromEnum :: a -> Int
( infix 4 ) applied to a value that is too large to fit in
an Int .
(<=) :: a -> a -> Bool
( infix 4 ) Used in Haskell’s translation of [n..]
with [n..] = enumFrom n , a possible
enumFrom :: a -> [a]
(>) :: a -> a -> Bool implementation being enumFrom n = n :
( infix 4 ) enumFrom (succ n) .
(>=) :: a -> a -> Bool Used in Haskell’s translation of [n,n'..]
( infix 4 ) with [n,n'..] = enumFromThen n n' , a
possible implementation being
max :: a -> a -> a enumFromThen :: a -> a -> [a]
`enumFromThen n n’ = n : n’ : worker (f x)
min :: a -> a -> a (f x n’), worker s v = v : worker s (s v), x =
fromEnum n’ - fromEnum n and f n y
The enumFrom … methods are used in Haskell’s translation of arithmetic sequences. Used in Haskell’s translation of
[n,n'..m] with [n,n'..m] =
Instances of Enum may be derived for any enumeration type (types whose constructors enumFromThenTo n n' m , a possible
enumFromThenTo :: a -> a -> a ->
have no fields). The nullary constructors are assumed to be numbered left-to-right by implementation being `enumFromThenTo
[a]
fromEnum from 0 through n-1 . n n’ m = worker (f x) (c x) n m, x =
fromEnum n’ - fromEnum n, c x = bool (>=)
For any type that is an instance of class Bounded as well as Enum , the following should ((x 0) f n y
hold: The calls succ maxBound and pred minBound should result in a runtime error.
fromEnum and toEnum should give a runtime error if the result value is not representable in
the result type. For example, toEnum 7 :: Bool is an error. enumFrom and enumFromThen
should be defined with an implicit bound.
Signature Docs
data Integer
minBound :: a
maxBound :: a Arbitrary precision integers. In contrast with fixed-size integral types such as Int , the
Integer type represents the entire infinite range of integers.
Integers are stored in a kind of sign-magnitude form, hence do not expect two’s complement
form when using bit operations.
data Float
Single-precision floating point numbers. It is desirable that this type be at least equal in
range and precision to the IEEE single-precision type.
data Double
Double-precision floating point numbers. It is desirable that this type be at least equal in
range and precision to the IEEE double-precision type.
The Haskell report defines no laws for Real , however Real instances are customarily
expected to adhere to the following law:
Signature Docs
The Haskell Report defines no laws for Integral . However, Integral instances are The Haskell Report defines no laws for Fractional . However, (+) and (*) are
customarily expected to define a Euclidean domain and have the following properties for the customarily expected to define a division ring and have the following properties:
div / mod and quot / rem pairs, given suitable Euclidean functions f and g :
recip gives the multiplicative inverse: x * recip x = recip x * x = fromInteger 1
x = y * quot x y + rem x y with rem x y = fromInteger 0 or g (rem x y) < g Totality of toRational : toRational is total
y
Coherence with toRational : if the type also implements Real , then fromRational is
x = y * div x y + mod x y with mod x y = fromInteger 0 or f (mod x y) < f y a left inverse for toRational , i.e. fromRational (toRational i) = i
An example of a suitable Euclidean function, for Integer’s instance, is abs . Note that it isn’t customarily expected that a type instance of Fractional implement a
field. However, all instances in base do.
In addition, toInteger should be total, and fromInteger should be a left inverse for it, i.e.
fromInteger (toInteger i) = i . Minimal complete definition: fromRational, (recip | (/))
quot :: a -> a -> a ( infixl 7 ) integer division truncated toward zero recip :: a -> a Reciprocal fraction.
integer remainder, satisfying (x `quot` Conversion from a Rational (that is
rem :: a -> a -> a ( infixl 7 )
y)*y + (x `rem` y) == x Ratio Integer ). A floating literal stands
fromRational :: Rational -> a for an application of fromRational to a
integer division truncated toward negative value of type Rational , so such literals
div :: a -> a -> a ( infixl 7 )
infinity have type (Fractional a) => a .
integer modulus, satisfying (x `div`
mod :: a -> a -> a ( infixl 7 )
y)*y + (x `mod` y) == x
⚠️ The functions quot , rem , div , mod , quotRem and divMod are partial (because they
throw when 0 is passed as the divisor) for all the integer types in base.
class Fractional a => Floating a where class (Real a, Fractional a) => RealFrac a where
Trigonometric and hyperbolic functions and related functions. Extracting components of fractions.
The Haskell Report defines no laws for Floating . However, (+) , (*) and exp are Minimal complete definition: properFraction
customarily expected to define an exponential field and have the following properties: exp
(a + b) = exp a * exp b , exp (fromInteger 0) = fromInteger 1 Signature Docs
Minimal complete definition: pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh, The function properFraction takes a
asinh, acosh, atanh real fractional number x and returns a
pair (n,f) such that x = n+f , and: n is
Signature Docs an integral number with the same sign as
properFraction :: Integral b => a -> x ; and f is a fraction with the same type
pi :: a (b, a) and sign as x , and with absolute value
less than 1 .
exp :: a -> a
The default definitions of the ceiling ,
log :: a -> a floor , truncate and round functions
are in terms of properFraction.
sqrt :: a -> a
truncate x returns the integer nearest
(**) :: a -> a -> a truncate :: Integral b => a -> b
x between zero and x
( infixr 8 )
round x returns the nearest integer to
logBase :: a -> a -> a round :: Integral b => a -> b x ; the even integer if x is equidistant
between two integers
sin :: a -> a
acos :: a -> a
atan :: a -> a
sinh :: a -> a
cosh :: a -> a
tanh :: a -> a
asinh :: a -> a
acosh :: a -> a
atanh :: a -> a
Signature Docs
class (RealFrac a, Floating a) => RealFloat a floatRadix :: a -> Integer
a constant function, returning the radix of
the representation (often 2 )
where
a constant function, returning the number
floatDigits :: a -> Int
of digits of floatRadix in the significand
Efficient, machine-independent access to the components of a floating-point number.
a constant function, returning the lowest
Minimal complete definition: floatRadix, floatDigits, floatRange, decodeFloat,
floatRange :: a -> (Int, Int) and highest values the exponent may
encodeFloat, isNaN, isInfinite, isDenormalized, isNegativeZero, isIEEE
assume
True if the argument is an IEEE “not-a- even :: Integral a => a -> Bool
isNaN :: a -> Bool
number” (NaN) value
odd :: Integral a => a -> Bool
True if the argument is an IEEE infinity or
isInfinite :: a -> Bool gcd x y is the non-negative factor of
negative infinity
both x and y of which every common
True if the argument is too small to be factor of x and y is also a factor; for
isDenormalized :: a -> Bool example gcd 4 2 = 2 , gcd (-4) 6 = 2 ,
represented in normalized format
gcd 0 4 = 4 . gcd 0 0 = 0 . (That is, the
True if the argument is an IEEE negative common divisor that is “greatest” in the
isNegativeZero :: a -> Bool gcd :: Integral a => a -> a -> a
zero divisibility preordering.)
True if the argument is an IEEE floating Note: Since for signed fixed-width integer
isIEEE :: a -> Bool types, abs minBound < 0 , the result may
point number
be negative if one of the arguments is
a version of arctangent taking two real minBound (and necessarily is if the other
floating-point arguments. For real floating is 0 or minBound ) for such types.
x and y , atan2 y x computes the
angle (from the positive x-axis) of the lcm x y is the smallest positive integer
lcm :: Integral a => a -> a -> a
vector from the origin to the point (x,y) . that both x and y divide.
atan2 y x returns a value in the range (^) :: (Num a, Integral b) => a -> b raise a number to a non-negative integral
[-pi, pi] . It follows the Common Lisp -> a infixr 8 power
atan2 :: a -> a -> a
semantics for the origin when signed
zeroes are supported. atan2 y 1 , with (^^) :: (Fractional a, Integral b)
raise a number to an integral power
y in a type that is RealFloat , should => a -> b -> a infixr 8
return the same value as atan y . A
⚠️
General coercion from Integral types.
default definition of atan2 is provided,
fromIntegral :: (Integral a, Num b) This function performs silent
but implementors can provide a more
=> a -> b truncation if the result type is not at least
accurate implementation.
as big as the argument’s type.
⚠️
General coercion to Fractional types.
This function goes through the
realToFrac :: (Real a, Fractional b) Rational type, which does not have
=> a -> b values for NaN for example. This means it
does not round-trip. For Double it also
behaves differently with or without -O0 .
>>> fmap show Nothing
Monads and functors
Nothing
A type f is a Functor if it provides a function fmap which, given any types a and b lets >>> fmap show (Left 17)
you apply any function from (a -> b) to turn an f a into an f b , preserving the Left 17
structure of f . Furthermore f needs to adhere to the following:
>>> fmap show (Right 17)
Identity: fmap id == id Right "17"
Composition: fmap (f . g) == fmap f . fmap g
>>> fmap (*2) [1,2,3]
Note, that the second law follows from the free theorem of the type fmap and the first law, [2,4,6]
so you need only check that the former condition holds.
>>> fmap even (2,2)
Minimal complete definition: fmap
(2,True)
Sequential application.
A functor with application, providing operations to embed pure expressions ( pure ), and (<*>) :: f (a -> b) -> f a -> f b A few functors support an implementation
sequence computations and combine their results ( <*> and liftA2 ). ( infixl 4 ) of <*> that is more efficient than the
default one.
A minimal complete definition must include implementations of pure and of either <*> or
liftA2 . If it defines both, then they must behave the same as their default definitions: Lift a binary function to actions.
(<*>) = liftA2 id , liftA2 f x y = f <$> x <*> y Some functors support an implementation
of liftA2 that is more efficient than the
Further, any definition must satisfy the following: liftA2 :: (a -> b -> c) -> f a -> f
default one. In particular, if fmap is an
b -> f c
Identity: pure id <*> v = v expensive operation, it is likely better to
use liftA2 than to fmap over the
Composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
structure and then use <*> .
Homomorphism: pure f <*> pure x = pure (f x)
Interchange: u <*> pure y = pure ($ y) <*> u (*>) :: f a -> f b -> f b Sequence actions, discarding the value of
( infixl 4 ) the first argument.
The other methods have the following default definitions, which may be overridden with
(<*) :: f a -> f b -> f a Sequence actions, discarding the value of
equivalent specialized implementations: u *> v = (id <$ u) <*> v , u <* v = liftA2
( infixl 4 ) the second argument.
const u v
As a consequence of these laws, the Functor instance for f will satisfy fmap f x = pure
f <*> x >>> data MyState = MyState {arg1 :: Foo, arg2 :: Bar, arg3 :: Baz}
>>> produceFoo :: Applicative f => f Foo
It may be useful to note that supposing forall x y. p (q x y) = f x . g y it follows >>> produceBar :: Applicative f => f Bar
from the above that liftA2 p (liftA2 q u v) = liftA2 f u . liftA2 g v >>> produceBaz :: Applicative f => f Baz
>>> mkState :: Applicative f => f MyState
If f is also a Monad , it should satisfy pure = return , m1 <*> m2 = m1 >>= (\x1 -> m2 >>> mkState = MyState <$> produceFoo <*> produceBar <*> produceBaz
>>= (\x2 -> return (x1 x2))) , (*>) = (>>) (which implies that pure and <*> satisfy
the applicative functor laws). >>> liftA2 (,) (Just 3) (Just 5)
Just (3,5)
Minimal complete definition: pure, ((<*>) | liftA2)
>>> Just 2 *> Just 3
Just 3
Furthermore, the Monad and Applicative operations should relate as follows: pure = The contribution of each element to the final result is combined with an accumulator via a
return , m1 <*> m2 = m1 >>= (\x1 -> m2 >>= (\x2 -> return (x1 x2))) suitable operator. The operator may be explicitly provided by the caller as with foldr or
may be implicit as in length . In the case of foldMap , the caller provides a function
The above laws imply: fmap f xs = xs >>= return . f , (>>) = (*>) and that pure and mapping each element into a suitable Monoid , which makes it possible to merge the per-
(<*>) satisfy the applicative functor laws. element contributions via that monoid’s mappend function.
The instances of Monad for lists, Maybe and IO defined in the Prelude satisfy these laws. A key distinction is between left-associative and right-associative folds:
Minimal complete definition: (>>=) In left-associative folds the accumulator is a partial fold over the elements that precede
the current element, and is passed to the operator as its first (left) argument. The
Signature Docs outermost application of the operator merges the contribution of the last element of the
structure with the contributions of all its predecessors.
Sequentially compose two actions,
passing any value produced by the first as In right-associative folds the accumulator is a partial fold over the elements that follow
(>>=) :: forall a b. m a -> (a -> m the current element, and is passed to the operator as its second (right) argument. The
an argument to the second.
b) -> m b outermost application of the operator merges the contribution of the first element of the
as >>= bs can be understood as the do
( infixl 1 ) structure with the contributions of all its successors.
expression
do { a <- as; bs a }
Recursive and corecursive reduction
Sequentially compose two actions,
discarding any value produced by the first, As observed in the above description of left and right folds, there are three general ways in
(>>) :: forall a b. m a -> m b -> m
like sequencing operators (such as the which a structure can be reduced to a summary value:
b
semicolon) in imperative languages. as
( infixl 1 ) Recursive reduction, which is strict in all the elements of the structure. This produces a
>> bs can be understood as the do
expression do { as; bs } single final result only after processing the entire input structure, and so the input must
be finite.
return :: a -> m a Inject a value into the monadic type. Corecursion, which yields intermediate results as it encounters additional input
elements. Lazy processing of the remaining elements makes the intermediate results
available even before the rest of the input is processed. The input may be unbounded,
and the caller can stop processing intermediate results early.
Short-circuit reduction, which examines some initial sequence of the input elements,
but stops once a termination condition is met, returning a final result based only on the
elements considered up to that point. The remaining elements are not considered. The
input should generally be finite, because the termination condition might otherwise
never be met.
Whether a fold is recursive, corecursive or short-circuiting can depend on both the method Signature Docs
chosen to perform the fold and on the operator passed to that method (which may be
implicit, as with the mappend method of a monoid instance). Map each element of the structure into a
monoid, and combine the results with
Strict recursive folds foldMap :: Monoid m => (a -> m) -> t (<>) . This fold is right-associative and
a -> m lazy in the accumulator. For strict left-
Common examples of strict recursive reduction are the various aggregate functions, like associative folds consider foldMap'
sum , product , length , as well as more complex summaries such as frequency counts. instead.
These functions return only a single value after processing the entire input structure. In such
cases, lazy processing of the tail of the input structure is generally not only unnecessary, but Right-associative fold of a structure, lazy
also inefficient. Thus, these and similar folds should be implemented in terms of strict left- in the accumulator.
associative Foldable methods (typically foldl' ) to perform an efficient reduction in In the case of lists, foldr, when applied to
constant space. a binary operator, a starting value
(typically the right-identity of the
Lazy corecursive folds operator), and a list, reduces the list using
the binary operator, from right to left:
Common examples of lazy corecursive reduction are functions that map and flatten a foldr f z [x1, x2, ..., xn] == x1
structure to a lazy stream of result values, like toList , concat and concatMap , i.e. an `f` (x2 `f` ... (xn `f` z)...)
iterator over the transformed input elements. In such cases, it is important to choose a Note that since the head of the resulting
Foldable method that is lazy in the tail of the structure, such as foldr (or foldMap , if the expression is produced by an application
result Monoid has a lazy mappend as with e.g. ByteString Builders). of the operator to the first element of the
foldr :: (a -> b -> b) -> b -> t a -
list, given an operator lazy in its right
> b
Short-circuit folds argument, foldr can produce a
terminating expression from an
Examples of short-circuit reduction include various boolean predicates that test whether unbounded list.
some or all the elements of a structure satisfy a given condition, like null , elem , and , For a general Foldable structure this
find , any . Because these don’t necessarily consume the entire list, they typically employ should be semantically identical to: foldr
foldr with an operator that is conditionally strict in its second argument. Once the
⚠️
f z = foldr f z . toList
termination condition is met the second argument (tail of the input structure) is ignored. No Applying foldr to infinite structures
result is returned until that happens. The key distinguishing feature of these folds is usually doesn’t terminate. It may still
conditional strictness in the second argument, it is sometimes evaluated and sometimes terminate under one of the following
not. conditions: the folding function is short-
circuiting; the folding function is lazy on
Minimal complete definition: foldMap | foldr its second argument
Signature Docs Signature Docs
⚠️
f z = foldl f z . toList
When it comes to lists, you always
want to use either foldl' or foldr
instead.
elem :: Eq a => a -> t a -> Bool Does the element occur in the structure?
( infix 4 ) Note: elem is often used in infix form.
>>> foldMap Sum [1, 3, 5] >>> foldr1 (+) [1..]
Sum {getSum = 9} -- * Hangs forever *
>>> foldr (||) False [False, True, False] >>> 3 `elem` ([4..] ++ [3])
True -- * Hangs forever *
>>> foldr (\c acc -> acc ++ [c]) "foo" ['a', 'b', 'c', 'd'] >>> maximum Nothing
"foodcba" *** Exception: maximum: empty structure
-- short circuiting
>>> foldr (||) False (True : repeat False)
True
until :: (a -> Bool) -> (a -> a) -> until p f yields the result of applying
a -> a f until p holds.
errorWithoutStackTrace :: forall (r
A variant of error that does not produce
:: RuntimeRep). forall (a :: TYPE r).
a stack trace.
[Char] -> a
O(n). filter , applied to a predicate and reverse xs returns the elements of xs
reverse :: [a] -> [a]
filter :: (a -> Bool) -> [a] -> [a] a list, returns the list of those elements in reverse order. xs must be finite.
that satisfy the predicate.
⚠️
which must be non-empty.
head :: HasCallStack => [a] -> a This function is partial. You can use
case-matching, uncons or listToMaybe
instead.
⚠️
which must be finite and non-empty.
last :: HasCallStack => [a] -> a This function is partial. You can use
reverse with case-matching, uncons or
listToMaybe instead.
⚠️
of a list, which must be non-empty.
tail :: HasCallStack => [a] -> [a]
This function is partial. You can use
case-matching or uncons instead.
⚠️
empty.
init :: HasCallStack => [a] -> [a]
This function is partial. You can use
reverse with case-matching or uncons
instead.
>>> last []
*** Exception: Prelude.last: empty list
>>> tail []
*** Exception: Prelude.tail: empty list
>>> init []
*** Exception: Prelude.init: empty list
>>> null []
True
any :: Foldable t => (a -> Bool) -> Determines whether any element of the >>> and (repeat True)
t a -> Bool structure satisfies the predicate. -- * Hangs forever *
all :: Foldable t => (a -> Bool) -> Determines whether all elements of the >>> or [False]
t a -> Bool structure satisfy the predicate. False
concat :: Foldable t => t [a] -> The concatenation of all the elements of a >>> or [True, True, False]
[a] container of lists. True
Map a function over all the elements of a
concatMap :: Foldable t => (a -> >>> any (> 3) [1,2]
container and concatenate the resulting
[b]) -> t a -> [b] False
lists.
>>> any (> 3) [1,2,3,4,5]
True
scanr1 :: (a -> a -> a) -> [a] -> O(n). scanr1 is a variant of scanr that >>> iterate (+3) 42
[a] has no starting value argument. [42,45,48,51,54,57,60,63...
take n , applied to a list xs , returns the >>> drop 3 [1,2]
take :: Int -> [a] -> [a] prefix of xs of length n , or xs itself if []
n >= length xs .
>>> takeWhile (< 3) [1,2,3,4,1,2,3,4]
drop n xs returns the suffix of xs after
[1,2]
drop :: Int -> [a] -> [a] the first n elements, or [] if n >=
length xs .
>>> dropWhile (< 3) [1,2,3,4,5,1,2,3]
takeWhile , applied to a predicate p and [3,4,5,1,2,3]
takeWhile :: (a -> Bool) -> [a] -> a list xs , returns the longest prefix
[a] (possibly empty) of xs of elements that >>> span (< 3) [1,2,3,4,1,2,3,4]
satisfy p . ([1,2],[3,4,1,2,3,4])
dropWhile :: (a -> Bool) -> [a] -> dropWhile p xs returns the suffix >>> break (> 3) [1,2,3,4,1,2,3,4]
[a] remaining after takeWhile p xs . ([1,2,3],[4,1,2,3,4])
O(min(m,n)). zip takes two lists and Splits the argument into a list of lines
returns a list of corresponding pairs. If stripped of their terminating \n
one input list is shorter than the other, lines :: String -> [String] characters. The \n terminator is optional
zip :: [a] -> [b] -> [(a, b)]
excess elements of the longer list are in a final non-empty line of the argument
discarded, even if one of the lists is string.
infinite. zip is right-lazy.
words breaks a string up into a list of
zip3 takes three lists and returns a list words, which were delimited by white
zip3 :: [a] -> [b] -> [c] -> [(a, b, of triples, analogous to zip . It is capable words :: String -> [String] space (as defined by isSpace ). This
c)] of list fusion, but it is restricted to its first function trims any white spaces at the
list argument and its resulting list. beginning and at the end.
liftA2 :: (a -> b -> c) -> f a -> f b -> f c isUpper :: Char -> Bool
liftA2 f x = (<*>) (fmap f x)
isAlpha :: Char -> Bool
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 f a b c = liftA2 f a b <*> c isAlpha :: Char -> Bool
liftM3 :: (Monad m) => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
liftM3 f m1 m2 m3 = do { x1 <- m1; x2 <- m2; x3 <- m3; return (f x1 x2 x3) }
scanl :: (b -> a -> b) -> b -> [a] -> [b] dropWhileEnd :: (a -> Bool) -> [a] -> [a]
scanl = scanlGo dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
where
scanlGo :: (b -> a -> b) -> b -> [a] -> [b] -- span p xs = (takeWhile p xs, dropWhile p xs)
scanlGo f q ls = q : (case ls of span :: (a -> Bool) -> [a] -> ([a],[a])
[] -> [] span _ xs@[] = (xs, xs)
x:xs -> scanlGo f (f q x) xs) span p xs@(x:xs')
| p x = let (ys,zs) = span p xs' in (x:ys,zs)
iterate :: (a -> a) -> a -> [a] | otherwise = ([],xs)
iterate f x = x : iterate f (f x)
-- break (> 3) [1,2,3,4,1,2,3,4] = ([1,2,3],[4,1,2,3,4])
repeat :: a -> [a] break :: (a -> Bool) -> [a] -> ([a],[a])
repeat x = xs where xs = x : xs break p = span (not . p)
replicate :: Int -> a -> [a] stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]
replicate n x = take n (repeat x) stripPrefix [] ys = Just ys
stripPrefix (x:xs) (y:ys)
cycle :: HasCallStack => [a] -> [a] | x == y = stripPrefix xs ys
cycle [] = errorEmptyList "cycle" stripPrefix _ _ = Nothing
cycle xs = xs' where xs' = xs ++ xs'
group :: Eq a => [a] -> [[a]]
group = groupBy (==) zipWith :: (a->b->c) -> [a]->[b]->[c]
groupBy :: (a -> a -> Bool) -> [a] -> [[a]] zipWith f = go
groupBy _ [] = [] where
groupBy eq (x:xs) = (x:ys) : groupBy eq zs go [] _ = []
where (ys,zs) = span (eq x) xs go _ [] = []
go (x:xs) (y:ys) = f x y : go xs ys
-- inits = map reverse . scanl (flip (:)) []
inits :: [a] -> [[a]] zipWith3 :: (a->b->c->d) -> [a]->[b]->[c]->[d]
zipWith3 z = go
tails :: [a] -> [[a]] where
go (a:as) (b:bs) (c:cs) = z a b c : go as bs cs
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool go _ _ _ = []
isPrefixOf [] _ = True
isPrefixOf _ [] = False unzip :: [(a,b)] -> ([a],[b])
isPrefixOf (x:xs) (y:ys) = x == y && isPrefixOf xs ys unzip = foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])
isSuffixOf :: (Eq a) => [a] -> [a] -> Bool lines :: String -> [String]
isInfixOf :: (Eq a) => [a] -> [a] -> Bool words :: String -> [String]
isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
unlines :: [String] -> String
-- The elements do not have to occur consecutively (but in order). unlines = concatMap (++ "\n")
isSubsequenceOf :: (Eq a) => [a] -> [a] -> Bool
isSubsequenceOf [] _ = True unwords :: [String] -> String
isSubsequenceOf _ [] = False unwords [] = ""
isSubsequenceOf a@(x:a') (y:b) | x == y = isSubsequenceOf a' b unwords ws = foldr1 (\w s -> w ++ ' ':s) ws
| otherwise = isSubsequenceOf a b
nub :: (Eq a) => [a] -> [a]
elem :: Eq a => a -> t a -> Bool nub = nubBy (==)
elem = any . (==) nubBy :: (a -> a -> Bool) -> [a] -> [a]
nubBy eq [] = []
filter :: (a -> Bool) -> [a] -> [a] nubBy eq (x:xs) = x : nubBy eq (filter (\ y -> not (eq x y)) xs)
filter _pred [] = []
filter pred (x:xs) delete :: (Eq a) => a -> [a] -> [a]
| pred x = x : filter pred xs delete = deleteBy (==)
| otherwise = filter pred xs deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
deleteBy _ _ [] = []
partition :: (a -> Bool) -> [a] -> ([a],[a]) deleteBy eq x (y:ys) = if x `eq` y then ys else y : deleteBy eq x ys
partition p xs = foldr (select p) ([],[]) xs
select :: (a -> Bool) -> a -> ([a], [a]) -> ([a], [a]) (\\) :: (Eq a) => [a] -> [a] -> [a]
select p x ~(ts,fs) | p x = (x:ts,fs) (\\) = foldl (flip delete)
| otherwise = (ts, x:fs)
union :: (Eq a) => [a] -> [a] -> [a]
zip :: [a] -> [b] -> [(a,b)] union = unionBy (==)
zip [] _ = [] unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
zip _ [] = [] unionBy eq xs ys = xs ++ foldl (flip (deleteBy eq)) (nubBy eq ys) xs
zip (a:as) (b:bs) = (a,b) : zip as bs
mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
mergeBy _ [] ys = ys
mergeBy _ xs [] = xs
mergeBy cmp (allx@(x:xs)) (ally@(y:ys))
| (x `cmp` y) <= EQ = x : mergeBy cmp xs ally
| otherwise = y : mergeBy cmp allx ys
-- Returns true if the given list contains any of the elements in the search
list.
hasAny :: Eq a => [a] -> [a] -> Bool
hasAny [] _ = False
hasAny _ [] = False
hasAny search (x:xs) = if x `elem` search then True else hasAny search xs
t1 [x ↦ t2 ]erhält man aus dem Term t1 , wenn man alle freien Vorkommen von x durch t2
ersetzt (Substitution). Substitution ist keine Textersetzung, sondern arbeitet auf der
Termstruktur, d. h. in der Textrepräsentation müssen ggf. Klammern ergänzt werden.
Ein Term, der nicht weiter reduziert werden kann, heißt in Normalform.
Auswertungsstrategien
Call-By-Name: Reduziere linkesten äußersten Redex, der nicht von einem λ umgeben ist.
Intuition: Reduziere Argumente erst, wenn benötigt. Standard-Auswertungsstrategie für
Funktionen in Haskell (Lazy Evaluation = Call-By-Name + Sharing).
Call-By-Value: Reduziere linkesten Redex, der nicht von einem λ umgeben ist und dessen
Argument ein Wert ist.
Intuition: Wertte Argumente von dem Funktionsaufruf aus.
Auswertungsstrategie in z. B. Java, C, Scheme, ML, … und bei arithmetischen Ausdrücken in
Haskell.
Call-By-Name und Call-By-Value werten nicht immer zur Normalform aus (z. B.
λx. (λy. y) x). Call-By-Name terminiert öfter.
Konfluenz Church-Kodierungen
Church-Rosser-Theorem: Der untypisierte λ-Kalkül ist konfluent, d. h. wenn t ⇒
∗
t1 und
t ⇒ t2 , dann gibt es ein t mit t1 ⇒ t und t2 ⇒ t . Church-Zahlen
∗ ′ ∗ ′ ∗ ′
Korollar: Die Normalform eines λ-Terms ist – sofern sie exisitert – eindeutig. c0 = λs. λz. z
Church-Booleans
if b then x else y wird zu b x y
ctrue = λt. λf . t
cf alse = λt. λf . f
Church-Paare
(a, b) wird zu λf . f a b
Church-Listen
nil = λn. λc. n
ω = (λx. x x) (λx. x x) ⇒ (λx. x x) (λx. x x) ⇒ ⋯ Für Typen τ 1 , … , τ n ist der Typ τ [α1 ↦ τ 1 , … , αn ↦ τ n ] eine Instanziierung vom
(λx. x x) (λy. f (y y)) ⇒
∗
f (f (f (f (…)))) Typschema ∀α1 . … ∀αn . τ . Schreibweise:
(∀α1 . … ∀αn . τ ) ⪰ τ [α1 ↦ τ 1 , … , αn ↦ τ n ], z. B. ∀α. α → α ⪰ int → int .
Rekursionsoperator
ohne Polymorphismus mit Polymorphismus
Rekursive Definition: g = λn. … g … n … (Rumpf verwendet g)
Funktional: G = λg. λn. … g … n …
β β β
Falls G einen Fixpunkt g ∗ hat, d. h. G g ∗ = g
∗
, so g ∗ = G g
∗
= λn. … g
∗
…n
Y f
⇐ f (Y f )
β
also f (Y f ) = Y f , d. h. Y f ist Fixpunkt von f
Beispiel Fakultät:
fak = Y G
Typinferenz für let -Polymorphismus
Typinferenz Typschema ta(τ , Γ) = ∀α1 . ∀α2 . … ∀αn . τ heißt Typabstraktion von τ relativ zu Γ,
wobei αi ∈ FV(τ ) ∖ FV(Γ).
Regel Constraint
1. Sammle Constraints aus linkem Teilbaum in Clet
2. Berechne den mgu σlet von Clet
3. Berechne Γ′ := σlet (Γ), x : ta(σlet (αj ), σlet (Γ))
αi = τ c
4. Benutze Γ in rechtem Teilbaum, sammle Constraints in Cbody
′
αj = αi
αi = αj → αk
αk = αj → αi
′
C ∪ Cbody ∪ {αk = αi }
let
Logische Programmierung
Logische Programmierung
Begriffe
Prolog Predicates
Comparison Operators
Backtracking
Cuts
Arithmetik
Unifikation und Resolution
Unifikationsalgorithmus nach Robinson (1965)
Begriffe Prolog Predicates
Fakten, z. B. liebt(fritz, fisch).
Terme werden als Fakten deklariert, per . . % list membership
member(X,[X|_]).
Terme
member(X,[_|Xs]) :- member(X,Xs).
Atome: fritz , fisch
beginnen mit Kleinbuchstaben
% list concatenation
Zahlen: 3 , 4.5 append([],X,X).
Variablen: X , Y1 , _X append([H|T],X,[H|S]) :- append(T,X,S).
beginnen mit Großbuchstaben oder Unterstrich
Term-Listen: 3,4.5,X,fritz % naive reverse
rev([],[]).
zusammengesetzt: liebt(fritz, fisch), liebt(fritz, X)
rev([X|R],Y) :- rev(R,Y1),append(Y1,[X],Y).
Atom am Anfang eines zusammengesetzten Terms heißt Funktor, z. B. liebt
Eine Gruppe von Fakten/Regeln mit gleichem Funktor und gleicher Argumentzahl im Regelkopf % efficient reverse
heißt Prozedur oder Prädikat rev(X,Y) :- rev1(X,[],Y).
rev1([],Y,Y).
Ein Prädikat heißt deterministisch, wenn es stets auf höchstens eine Weise erfüllt werden rev1([X|R],A,Y) :- rev1(R,[X|A],Y).
kann; hat es möglicherweise mehrere Lösungen, so heißt es nichtdeterministisch.
% Quicksort
qsort([],[]).
qsort([X|R],Y) :- split(X,R,R1,R2), qsort(R1,Y1), qsort(R2,Y2), append(Y1,
[X|Y2],Y).
split(X,[],[],[]).
split(X,[H|T],[H|R],Y) :- X>H, split(X,T,R,Y).
split(X,[H|T],R,[H|Y]) :- X=<H, split(X,T,R,Y).
% permutations
permute([],[]).
permute([X|R],P) :- permute(R,P1), append(A,B,P1), append(A,[X|B],P).
% natural numbers
nat(0).
nat(X) :- nat(Y), X is Y+1.
% dictionary lookup
lookup(N,[(N,A)|_],A1) :- !, A=A1.
lookup(N,[_|T],A) :- lookup(N,T,A).
% list uniqueness
allDifferent([]) :- !.
allDifferent([H|T]) :- not(member(H,T)), allDifferent(T).
Comparison Operators
Comparison Definition Evaluates Backtracking
X = Y succeeds if X and Y unify (match)
grandparent(X,Y)
X \= Y succeeds if X and Y do not unify; i.e. if not (X = Y) ┌─────────────┐
call ───►│ X=fritz Y= ├───► success
succeeds if terms T1 and T2 are identical; e.g. names of │ │
T1 == T2
variables have to be the same fail ◄───┤ ─►2 │◄─── redo
└─────────────┘
T1 \== T2 succeeds if terms T1 and T2 are not identical
E1 =:= E2 succeeds if values of expressions E1 and E2 are equal ✓
jedes Teilziel, das noch nicht endgültig fehlgeschlagen ist, als Box
E1 =\= E2 succeeds if values of expressions E1 and E2 are not equal ✓ in der Box Substitution, die beim Unifizieren des Teilziels mit Regelkopf entstand
succeeds if numeric value of expression E1 is < numeric Unterbäume der Box sind Teilziele im Rumpf der verwendeten Regel
E1 < E2 ✓
value of E2 Choice-Point ist nächste zu probierende Regel bei Reerfüllungsversucht
Arithmetische Ausdrücke unifizieren nicht mit Konstanten. Unifikationsalgorithmus nach Robinson (1965)
unify(∅) = [ ]
′
⎧ unify(C ) θl == θr
⎪
⎪
⎪
⎪ unify([Y ⇒ θ ] C ′ ) ∘ [Y ⇒ θ ]
⎪ r r θl == Y ∧ Y ∉ FV(θr )
⎪
⎪
⎪ ′
unify([Y ⇒ θl ] C ) ∘ [Y ⇒ θl ] θr == Y ∧ Y ∉ FV(θl )
′
unify({θl = θr } ⊎ C ) = ⎨ ′ 1 1 n n 1 1
unify(C ∪ {θ = θr , … , θ = θr }) θl = f (θ , … , θ )
⎪ l l l l
⎪
⎪
⎪ 1 n
⎪ ∧ θr = f (θr , … , θr )
⎪
⎪
⎩
⎪
f ail otherwise
Theorem: unify(C ) terminiert und gibt mgu für C zurück, falls C unifizierbar, ansonsten f ail.
Parallelprogrammierung
Parallelprogrammierung
Fundamentals
Amdahl’s Law
Flynn’s Taxonomy
Message Passing Interface (MPI)
Compilation & Execution
Communicators
Setup
Synchronization
Message Exchange
Communication Modes
Data Distribution
Java
Useful classes in java.base
Lambda Expressions
Functional Interfaces
Method References
Thread Programming
Happens-Before
Advanced
Design By Contract & Java Modeling Language (JML)
Liskov Substitution Principle
Fundamentals Message Passing Interface (MPI)
return 0;
}
Temporal Sorting of Messages: There is no global order on communication events, but
Synchronization messages are non-overtaking, i.e. sorted in time between one sender-receiver pair (this is only
true if a matching “receive” is available). Bottom line: first matching receive is served.
Fairness in receiving (from different senders) is not guaranteed.
int MPI_Barrier(MPI_Comm comm)
Communication Modes
Blocks until all processes have called it in order to make sure that all processes have reached Synchronous Send ( MPI_Ssend ): No buffer, synchronization (both sides wait for each
a certain point. other)
Send can be initiated before receive was started
int i; Matching receive must be posted, before operation sends anything (“rendezvous
for (i = 0; i < size; i++) { semantics”, non-local operation) – no buffer used
MPI_Barrier(MPI_COMM_WORLD); Can be used for synchronization instead of a barrier
if (i == myrank) { Safest and most portable option
printf("Hello World, I have rank %d out of %d.\n", myrank, size); Order of send and receive operation is irrelevant
} No hidden internal buffers are used
}
Potentially high synchronization overhead
Operation is non-local: matching receive is required
Message Exchange Buffered Send ( MPI_Bsend ): Explicit buffering, no synchronization (no wait for each
other)
Can complete before receive even starts (local operation)
int MPI_Send(void* buffer, int count, Explicit user-defined buffer is used, allocation with MPI_Buffer_attach
MPI_Datatype datatype, int dest, int tag, MPI_Comm Error, if buffer overflow (especially if no receive follows)
No synchronization overhead, but extra copy
comm) Ready Send ( MPI_Rsend ): No buffer, no synchronization, matching receive must already
be initiated
Receive must initiate before send starts (user is responsible for writing a correct
int MPI_Recv(void* buffer, int count, program) – if not, behavior is undefined
MPI_Datatype datatype, int source, int tag, No buffer used, data is transferred directly to the receiver
Lowest sender overhead
MPI_Comm comm, MPI_Status* status) No synchronization, no waiting for receiver
No extra copy to a buffer
Both functions are blocking and synchronous, i.e. no synchronous sending/receiving Standard Send ( MPI_Send ): May buffer or not, can be synchronous (implementation
necessary. MPI_Send blocks until the message buffer can be reused. MPI_Recv blocks until dependent)
the message is received in the buffer completely. Sends message with unknown receive state
May buffer: buffer may be on sender, receiver or both sides
void* buffer : the initial memory address of the sender’s / receiver’s buffer
May be synchronous: gets synchronous if hidden buffer size exceeded
int count : number of elements to send / receive
Can lead to unexpected timing behavior
MPI_Datatype datatype : type of buffer’s elements (needs to be specified explicitly)
int source : rank of the source process; wildcard MPI_ANY_SOURCE possible There is only one receive mode; it matches all four sending modes.
int dest : rank of the destination process
int tag : “context” of the message, e.g. a conversion ID; in MPI_Recv wildcard MPI_Sendrecv is blocking, but internally parallel in MPI (like threads with join). Send buffers
and receive buffers must be disjoint. MPI_Sendrecv_replace available with one buffer for
MPI_ANY_TAG possible
send and receive. Can be seen as “OUTIN” parameter semantics.
MPI_Comm comm : communicator of the process group, e.g. MPI_COMM_WORLD
MPI_Status* status : contains source and tag (which may be unknown due to
wildcards) in status.MPI_SOURCE and status_MPI_TAG
int MPI_Isend(void* buf, int count, MPI_Datatype int *msg, msglen, flag=0;
MPI_Status status;
type, int dest, int tag, MPI_Comm comm, MPI_Request request;
MPI_Request* request) // ...
MPI_Irecv(msg, msglen, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD,
int MPI_Irecv(void* buf, int count, MPI_Datatype &request);
// ...
type, int src, int tag, MPI_Comm comm, while (flag==0) {
MPI_Request* request) MPI_Test(&request, &flag, &status);
// waiting for a message, the receiver can perform other operations here
}
Orthogonal to the communication modes, communication can be blocking or non-blocking:
request is a pointer to status information about the operation. It can be used to check the
send and receive operations for completion:
A0 A1 A2 A0
int MPI_Bcast(void* buffer, int count, all receivers get equal-sized
Scatter A1
MPI_Datatype t, int root, MPI_Comm comm) but content-different data
A2
Before if (myrank == 0) {
After
Operation (⇨ data, ⇩ avgtime /= numprocs;
(⇨ data, ⇩ processes)
processes) printf("Min: %lf Max: %lf Avg: %lf\n", mintime, maxtime, avgtime);
}
A0 ◇ B0 ◇ A1 ◇ B1 ◇ A2 ◇ B2 ◇
A0 A1 A2
C0 C1 C2
Reduce B0 B1 B2
int MPI_Scatterv(void* sendbuf, int* sendcounts,
C0 C1 C2
int* displacements, MPI_Datatype sendtype, void*
A0 ◇ B0 ◇ A1 ◇ B1 ◇ A2 ◇ B2 ◇
recvbuf, int recvcount, MPI_Datatype recvtype, int
A0 A1 A2
C0 C1 C2 root, MPI_Comm comm)
A0 ◇ B0 ◇ A1 ◇ B1 ◇ A2 ◇ B2 ◇
Allreduce B0 B1 B2
C0 C1 C2
int MPI_Gatherv(void* sendbuf, int sendcount,
C0 C1 C2
A0 ◇ B0 ◇ A1 ◇ B1 ◇ A2 ◇ B2 ◇
C0 C1 C2 MPI_Datatype sendtype, void* recvbuf, int*
recvcounts, int* displacements, MPI_Datatype
A0 A1 A2 A0 ◇ B0 ◇ C0
recvtype, int root, MPI_Comm comm)
Reducescatter B0 B1 B2 A1 ◇ B1 ◇ C1
Inserts the specified element at the specified static <T> Optional<T> empty() Returns an empty Optional instance.
void add(int index, E element)
position in this list (optional operation).
If a value is present, returns the value, otherwise
T get()
Inserts all of the elements in the specified throws NoSuchElementException .
boolean addAll(int index,
collection into this list at the specified
Collection<? extends E> c) void ifPresent(Consumer<? super If a value is present, performs the given action
position (optional operation).
T> action) with the value, otherwise does nothing.
Returns the element at the specified position
E get(int index) void ifPresentOrElse(Consumer<? If a value is present, performs the given action
in this list.
super T> action, Runnable with the value, otherwise performs the given
Returns the index of the first occurrence of emptyAction) empty-based action.
int indexOf(Object o) the specified element in this list, or -1 if this
list does not contain the element. If a value is not present, returns true , otherwise
boolean isEmpty()
false .
Returns the index of the last occurrence of the
int lastIndexOf(Object o) specified element in this list, or -1 if this list If a value is present, returns true , otherwise
boolean isPresent()
does not contain the element. false .
static <E> List<E> of(E... Returns an unmodifiable list containing an If a value is present, returns an Optional
elements) arbitrary number of elements. <U> Optional<U> map(Function<? describing (as if by ofNullable(T) ) the result
super T,? extends U> mapper) of applying the given mapping function to the
void replaceAll(UnaryOperator<E> Replaces each element of this list with the value, otherwise returns an empty Optional .
operator) result of applying the operator to that element.
static <T> Optional<T> of(T Returns an Optional describing the given non-
Replaces the element at the specified position value) null value.
E set(int index, E element) in this list with the specified element (optional
operation). Returns an Optional describing the given value,
static <T> Optional<T>
if non-null, otherwise returns an empty
ofNullable(T value)
int size() Returns the number of elements in this list. Optional .
void sort(Comparator<? super E> Sorts this list according to the order induced Optional<T> or(Supplier<? If a value is present, returns an Optional
c) by the specified Comparator . extends Optional<? extends T>> describing the value, otherwise returns an
supplier) Optional produced by the supplying function.
Returns a view of the portion of this list
List<E> subList(int fromIndex, int
between the specified fromIndex , inclusive, If a value is present, returns the value, otherwise
toIndex) T orElse(T other)
and toIndex , exclusive. returns other .
thread thread
@FunctionalInterface created starts runnable wake up or
new
interface Predicate { (running/ready) unblocked
boolean check(int value);
}
termination,
abortion,
crash
public int sum(List<Integer> values, Predicate predicate) {
int result = 0;
// Could be expressed even more compact with streams terminated
for (int value : values) {
if (predicate.check(value)) result += value;
}
return result;
} @FunctionalInterface interface java.lang.Runnable
Method
sum(values, i -> i > 5); Description
Signature
Streams do not require additional memory. Results of stream operations are calculated on
volatile ensures that changes to variables are immediately visible to all threads / demand. Stream operations are executed lazy. Any Java collection can be treated as a stream
processors
by calling the stream() method.
volatile establishes a happens-before relationship: a write to a volatile variable
happens-before every subsequent read to that variable interface java.util.stream.BaseStream<T,S extends BaseStream<T,S>>
This means that all writes to (potentially different) variables before writing a volatile
variable are visible to all reads of that variables after reading the volatile variable, https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/BaseStream
because statements within a thread have a happens-before relationship in their program .html (https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/BaseStream.html)
order
T - the type of the stream elements
Values are not locally cached in a CPU cache (every read/write directly back to main
S - the type of the stream implementing BaseStream
memory)
Compiler/Processor optimizations are disabled: instruction reordering is not possible for
Method Signature Description
the volatile variable
Closes this stream, causing all close handlers for this stream
void close()
Advanced pipeline to be called.
executorService.shutdown();
Method Signature Description
interface java.util.stream.Stream<T> extends BaseStream<T, Stream<T>>
boolean allMatch(Predicate<? super Returns whether all elements of this stream
https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html T> predicate) match the provided predicate.
(https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html)
boolean anyMatch(Predicate<? super Returns whether any elements of this
T> predicate) stream match the provided predicate.
Returns an LongStream consisting of the Optional<T> min(Comparator<? super Returns the minimum element of this stream
LongStream flatMapToLong(Function<? results of replacing each element of this T> comparator) according to the provided Comparator .
super T,? extends LongStream> stream with the contents of a mapped boolean noneMatch(Predicate<? super Returns whether no elements of this stream
mapper) stream produced by applying the provided T> predicate) match the provided predicate.
mapping function to each element.
Returns a sequential Stream containing a
void forEach(Consumer<? super T> Performs an action for each element of this static <T> Stream<T> of(T t)
single element.
action) stream.
static <T> Stream<T> of(T... Returns a sequential ordered stream whose
Performs an action for each element of this values) elements are the specified values.
void forEachOrdered(Consumer<?
stream, in the encounter order of the stream
super T> action)
if the stream has a defined encounter order. Returns a sequential Stream containing a
static <T> Stream<T> ofNullable(T
single element, if non-null, otherwise returns
Returns an infinite sequential unordered t)
static <T> Stream<T> an empty Stream .
stream where each element is generated by
generate(Supplier<? extends T> s)
the provided Supplier . Returns a stream consisting of the elements
of this stream, additionally performing the
Returns a sequential ordered Stream Stream<T> peek(Consumer<? super T>
provided action on each element as
static <T> Stream<T> iterate(T produced by iterative application of the action)
elements are consumed from the resulting
seed, Predicate<? super T> hasNext, given next function to an initial element, stream.
UnaryOperator<T> next) conditioned on satisfying the given
hasNext predicate. Performs a reduction on the elements of
Optional<T> this stream, using an associative
Returns an infinite sequential ordered reduce(BinaryOperator<T> accumulation function, and returns an
Stream produced by iterative application of accumulator) Optional describing the reduced value, if
static <T> Stream<T> iterate(T
a function f to an initial element seed, any.
seed, UnaryOperator<T> f)
producing a Stream consisting of seed ,
f(seed) , f(f(seed)) , etc. Performs a reduction on the elements of
T reduce(T identity, this stream, using the provided identity value
Returns a stream consisting of the elements BinaryOperator<T> accumulator) and an associative accumulation function,
Stream<T> limit(long maxSize) of this stream, truncated to be no longer and returns the reduced value.
than maxSize in length.
<U> U reduce(U identity,
Returns a stream consisting of the results of Performs a reduction on the elements of
<R> Stream<R> map(Function<? super BiFunction<U,? super T,U>
applying the given function to the elements this stream, using the provided identity,
T,? extends R> mapper) accumulator, BinaryOperator<U>
of this stream. accumulation and combining functions.
combiner)
DoubleStream Returns a DoubleStream consisting of the Returns a stream consisting of the
mapToDouble(ToDoubleFunction<? super results of applying the given function to the remaining elements of this stream after
T> mapper) elements of this stream. Stream<T> skip(long n)
discarding the first n elements of the
stream.
Method Signature Description
interface IntStream extends BaseStream<Integer,IntStream>
Returns a stream consisting of the elements
Stream<T> sorted() of this stream, sorted according to natural https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/IntStream.ht
order. ml (https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/IntStream.html)
Accumulates the elements of this stream Returns an OptionalInt describing the minimum
List<T> toList() OptionalInt min() element of this stream, or an empty optional if this
into a List .
stream is empty.
java.util.concurrent.atomic.AtomicInteger
R collect(
Supplier<R> supplier, // delivers a new result container Method Signature Description
BiConsumer<R,? super T> accumulator, // incorporates new element in the result
BiConsumer<R,R> combiner // combines two values compatible with the result int get() Just return the variable.
)
int incrementAndGet() Atomically increments and returns the variable.
Pseudocode behavior: int decrementAndGet() Atomically decrements and returns the variable
Checks for the old value and replaces it with the new
boolean
R result = supplier.get(); one. If successful, true is returned. If the
compareAndSet(int
for (T element : this stream) AtomicInteger does not contain the oldValue , false
oldValue, int newValue)
accumulator.accept(result, element); is returned and nothing is done.
return result;
Fork-Join
combiner is only used for parallel streams to combine results calculated in parallel.
Fork-Join is a pattern for effectively computing divide-and-conquer
algorithms in parallel. Problems are solved by splitting them into subtasks, solving them in
Map<Integer, List<Person>> groupedByAge = parallel and finally composing the results.
personsInAuditorium
.stream() General algorithm in pseudocode:
.collect(Collectors.groupingBy(Person::getAge));
Result solve(Problem problem) {
if (problem is small enough) {
Atomicity directly solve problem
} else {
Atomic statements are executed complete or not at all, produce no visible side effects until
split problem into independent parts
their completion and are the smallest unit of code that cannot be interleaved (no race
fork new subtasks to solve each part
conditions due to interleaving) but can still produce synchronization errors due to memory
join all subtasks
consistency errors (missing happens-before relationship).
compose results from subresults
Atomic statements in Java: }
}
Reads and writes of reference variables
Reads and writes of most primitive variables (potentially not for long and double )
Reads and writes of all variables declared volatile // ...
ForkJoinPool fjPool = new ForkJoinPool();
MyTask myTask = new MyTask(/* ... */);
fjPool.invoke(myTask);
// ...
receiveBuilder()
// ForkJoinPool can execute ForkJoinTask match(Class<P> type, UnitApply<P> apply)
// concretizations of ForkJoinTask are
match(Class<P> type, TypedPredicate<P> predicate, UnitApply<P> apply)
// * RecursiveAction (no result)
// * RecursiveTask (returns result) matchAny(UnitApply<Object> apply)
public class MyTask extends RecursiveTask<Integer> { Receive build()
private int[] array; preStart()
private static final int THRESHOLD = 20; postStop()
preRestart() , postRestart()
public MyTask(int[] arr) {
this.arr = arr; ActorRef getSelf()
} ActorContext getContext()
ActorRef getSender()
// ForkJoinTasks must override compute()
Props Props.create(Class<?> actorType, Object... parameters)
@Override
public Integer compute() { ActorRef actorOf(Props props)
// split task if it is bigger than the threshold tell(Object message, ActorRef sender)
if (arr.length > THRESHOLD) { Future<?> Patterns.ask( ActorRef target, Object msg, Timeout timeout)
return ForkJoinTask.invokeAll(createSubtasks()) T Await.result(Future<T> future, Duration duration)
.stream().mapToInt(ForkJoinTask::join).sum(); stop(ActorRef actorToStop)
// join subtasks ^
PoisonPill.getInstance()
} else {
return processing(arr);
} public class HelloWorldActor extends AbstractActor {
} @Override
public Receive createReceive() {
private Collection<CustomRecursiveTask> createSubtasks() { return receiveBuilder()
// divide array into smaller parts, e.g., two halves .match(String.class,
} message -> message.equals("printHello"),
message -> System.out.println("Hello World!"))
private Integer processing(int[] arr) { .matchAny(message -> unhandled(message))
// do actually interesting computation, e.g., calculate average of .build();
array }
} }
}
class Stack {
//@ invariant size >= 0 && size <= 10;
private Object[] elements = new Object[10];
private /*@ spec_public @*/ int size = 0;
// ...
}
A special case of this is known from parameter and return types of methods
(Co-/Contravariance).
Compiler
Compiler
Syntaktische Analyse (Parsing)
Grammatiken
Rekursiver Abstieg
Semantische Analyse
Namensanalyse
Java Bytecode
Activation Records (Stackframe)
Deskriptoren
Methodenaufruf
Objekterzeugung und -initialisierung
Array anlegen und Arrayzugriff
Feldzugriff
Codeerzeugung
Umgekehrte Polnische Notation (UPN)
Kontrollfluss
Nach der Umformung möchten wir eine SLL(k) -Grammatik mit möglichst kleinem k
(Effizienz), die eindeutig ist und die Operatorpriorität und -assoziativität umsetzt.
Syntaktische Analyse (Parsing)
Präzedenz
Grammatiken
Für jede Präzedenzstufe (mind.) ein Nichtterminal. Produktionen von niedriger zu hoher
Eine Grammatik G = (Σ, V , P , S ) besteht aus einem endlichen Alphabet Σ (Terminale), Priorität.
einer endlichen Menge V von Variablen mit V ∩ Σ = ∅ (Nichtterminale), einem
Expr → Expr + Term ∣ Term
Startsymbol S ∈ V und einer endlichen Menge P von Produktionsregeln der Form l → r
Term → Term + Factor ∣ Factor
mit l ∈ (V ∪ Σ)+ , r ∈ (V ∪ Σ)∗ .
Factor → ( Expr ) ∣ id
Eine Grammatik heißt kontextfrei, wenn jede Produktion die Form A → α mit A ∈ V hat.
Linksfaktorisierung
L(G) = {ω ∈ Σ
∗
∣ S ⇒
∗
ω} ist die Menge aller in der Grammatik ableitbaren Wörter.
Für ein Nichtterminal A längstes gemeinsames Präfix α (mind.) zweier Alternativen
Bei einer Linksableitung (⇒ L ) bzw. Rechtsableitung (⇒ R ) wird eine Produktion stets auf bestimmen.
das linkeste bzw. rechteste Nichtterminal angewendet.
Wenn nichtrivial (α ≠ ε ), Produktionen A → αβ1 ∣ αβ2 ∣ ⋯ durch A → αA
′
,
SLL-Grammatiken A
′
→ β1 ∣ β2 ∣ ⋯ (mit neuem Nichtterminal A′ ) ersetzen.
Für χ ∈ Σ∗ ist der k -Anfang k : χ der Präfix der Länge k von χ### …, wobei # ∈ Σ Eliminierung von Linksrekursion
das Ende der Terminalfolge kennzeichnet und ansonsten nicht in der Grammatik auftritt.
Produktionen A → Aα ∣ β durch A → βA
′
, A′ → αA
′
∣ ε (mit neuem Nichtterminal A′ )
Für χ ∈ (Σ ∪ V )
+
sind ersetzen.
Firstk (χ) = {β ∣ ∃τ ∈ Σ
∗
: χ ⇒
∗
τ ∧ β = k : τ} Zu jeder kontextfreien Grammatik existiert äquivalente kontextfreie Grammatik ohne
Linksrekursion.
die k -Anfänge der Strings, die aus χ generiert werden können, und
Assoziativität von Operatoren (Beispiel a + b + c ):
Linksassoziativ: (a + b) + c bei Linksrekursion (E → E + T )
∗ ∗
Followk (χ) = {β ∣ ∃α, ω ∈ (V ∪ Σ) mit S ⇒ αχω ∧ β ∈ Firstk (ω)}
3. Für jede Produktion A → αB füge Follow(A) zu Follow(B) hinzu. Uneindeutig z. B. bei if A then if B then C else D (in der Praxis gehört else
4. Für jede Produktion A → αBβ mit β ⇒
∗
ε füge Follow(A) zu Follow(B) hinzu. i. d. R. zum innersten if).
5. Wiederhole bis sich keine Follow-Menge mehr ändert. Nicht zweimal dasselbe NIchtterminal auf der rechten Seite, um Mehrdeutigkeit zu
vermeiden.
Die Indizmenge von A → α ist mit k Token Lookahead genau Firstk (α Followk (A)).
Eine (kontextfreie) Grammatik ist genau dann eine SLL(k) -Grammatik, wenn für alle Paare
von Produktionen A → α ∣ β mit α ≠ β, gilt:
Grammar Engineering
Ohne Abstract Syntax Tree:
Rekursiver Abstieg
void parseTList() {
Genau dann, wenn G eine SLL(k) -Grammatik ist, ist G mit k -Lookahed im rekursiven
switch(lexer.current) {
Abstieg parsebar. case STAR:
expect(TokenType.STAR);
Pro Nichtterminal eine Parse-Funktion, die Lookahead inspiziert und darauf basierend
parseF();
Produktion anhand derer Indizmengen auswählt. Falls k = 1 Produktionsauswahl mit
parseTList();
switch über aktuelles Token. break;
case SLASH:
void expect(TokenType e) { expect(TokenType.SLASH);
if (lexer.current == e) { parseF();
lexer.lex(); parseTList();
} else { break;
error(); case PLUS:
} case MINUS:
} case R_PAREN:
case EOF:
break;
Arithmetische Ausdrücke default:
error("Expected one of */+-)# but got ", lexer.current);
T → F TList }
TList → * F TList ∣ / F TList ∣ ε }
F → id ∣ ( E )
void parseT() {
parseF();
paresTList();
}
Expr parseT() {
Expr leftmost = parseF();
return paresTList(leftmost);
}
2. Unmittelbar vor Eintritt in Bereich 1
Namenskeller:
Semantische Analyse
Bereich Liste … …
0 Stringtabelle:
Index Bezeichner Definition
Stringtabelle:
0 foo Zeile 2
Index Bezeichner Definition
1 bar Zeile 3
0 foo —
1 bar —
Stack Stack
Mnemonic Description Forms
Java Bytecode (before) (after)
d2f , d2i ,
https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-6.html
d2l , f2d ,
(https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-6.html)
f2i , f2l ,
i2b , i2c ,
Prefix ( ? ) Type ?2? …, value …, result Convert int to byte .
i2d , i2f ,
a Reference i2l , i2s ,
l2d , l2f ,
b byte / boolean (often represented using int ) l2i
c char Push the null object
aconst_null … …, null reference onto the
s short
operand stack.
i int
The values are popped
l long from the operand
stack. The ? result is
f float …, value1, dadd , fadd ,
?add …, result value1 + value2 .
value2 iadd , ladd
The result is pushed
d double
onto the operand
stack.
aaload ,
baload ,
caload ,
…, arrayref, daload ,
?aload …, value Load ? from array.
index faload ,
iaload ,
laload ,
saload
An ? result is
calculated by taking
the bitwise AND
…, value1,
?and …, result (conjunction) of value1 iand , land
value2
and value2. The result
is pushed onto the
operand stack.
Stack Stack Stack Stack
Mnemonic Description Forms Mnemonic Description Forms
(before) (after) (before) (after)
if_acmpeq ,
if_acmpne ,
if_? if_icmpeq ,
comp<cond> …, value1, Branch if ? if_icmpne ,
…
<branchbyte1> value2 comparison succeeds. if_icmplt ,
<branchbyte2> if_icmpge ,
if_icmpgt ,
if_icmple
Stack Stack Stack Stack
Mnemonic Description Forms Mnemonic Description Forms
(before) (after) (before) (after)
aload_<n> ,
The ? in the local
dload_<n> ,
?load_<n> variable at n is
… …, value fload_<n> ,
(0 ≤ n ≤ 3) pushed onto the
iload_<n> ,
operand stack.
lload_<n>
Stack Stack Stack Stack
Mnemonic Description Forms Mnemonic Description Forms
(before) (after) (before) (after)
An ? Namen von Typen, Feldern und Methoden müssen einem festgelegtem
result is Schema entsprechen.
calculated
by taking Art Schema
the bitwise
exclusive Objekttypen: java.lang.Object Ljava/lang/Object;
OR of ixor ,
?xor Primitive Typen: int , void , boolean , … I , V , Z , …
value1 and lxor
value2. foo(ILjava/lang/Object;)V
The result Methoden: void foo(int, Object)
(identifiziert über Name × Deskriptor)
is pushed
onto the b:Z
Felder: boolean b
operand (identifiziert über Name)
stack.
Konstruktoren Name: <init> , Static Initializer: <clinit>
int foo(int i) {
return i; class Test {
} // jede Klasse braucht einen Konstruktor (sonst Defaultkonstruktor)
} Test foo() {
return new Test();
}
}
int bar();
aload_0
bipush 42
invokevirtual #2 // s. Konstantenpool Test();
ireturn aload_0
invokespecial #1;
int foo(int); return
iload_1
ireturn Test foo();
new #2;
dup
Konstantenpool: invokespecial #3;
areturn
#2 = Method #3.#16
#3 = class #17 Konstantenpool:
#11 = Asciz foo
#12 = Asciz (I)I
#16 = NameAndType #11:#12 #1 java/lang/Object.<init>()V
#17 = Asciz Test #2 Test
#3 Test.<init>()V
Array anlegen und Arrayzugriff Codeerzeugung
int[] array = new int[10];
array[7] = 42; Umgekehrte Polnische Notation (UPN)
Natürliche Darstellung für Stackmaschinen (entspricht der Befehlsreihenfolge von Java
Bytecode). Eindeutig, auch ohne Präzedenzen und Klammern.
bipush 10 // Konstante 10
newarray int // Array anlegen vom Typ int
astore 1 // in Variable array (var 1) speichern Ausdruck Ausdruck in UPN
aload 1 // Variable array (var 1) laden 7 * 4 7 4 *
bipush 7 // Konstante 7
bipush 42 // Konstante 42 2 * (2 + 3) 2 2 3 + *
iastore // Wert (42) in Array−Index (7) von Array ("array") schreiben
return // Aus Funktion zurueckkehren 5 + y + 3 * 5 5 y + 3 5 * +
writer.printf("%s:", thenLabel);
Kontrollfluss this.thenStatement.codeGen()
Jeder AST-Knoten ruft rekursiv codeGen() -Methode seiner Kinder auf.
String afterLabel = makeLabel();
Jede Kontrollstruktur generiert ihren Code so, dass sie nur “per fall-through” verlassen writer.printf(" goto %s", afterLabel);
wird.
Hintereinander ausgeführte codeGen() -Methoden generieren nacheinander writer.printf("%s:", elseLabel);
ausgeführten Code! if (this.elseStatement != null)
Codeerzeugung für Bedingungen (z. B. <= ) bekommt zwei Sprungziele übergeben, zu this.elseStatement.codeGen();
denen dann der erzeugte Code je nach Laufzeitwert der Bedingung springt.
writer.printf("%s:", afterLabel);
}
Ergänzung zu Codegen für Ausdrücke:
Kurzauswertung
class BinOp extends Expr { 1. Erzeuge Label l , das angesprungen wird, falls rechte Seite ausgewertet werden muss.
public void codeGen() { /* ... */ } // Arithmetische Ausdrücke, s. o.
2. Code für rechte Seite hinter l platzieren.
public void codeGen(String trueLabel, String falseLabel) {
String cmpsuffix = null; void genShortCircuitAnd(String definitelyTrue, String definitelyFalse) {
switch (this.opType) { // label to jump to if right side must also be evaluated
case LOGICALAND: genShortCircuitAnd(trueLabel, falseLabel); String unsureYet = makeLabel();
return;
case LOGICALOR: genShortCircuitOr(trueLabel, falseLabel); this.left.codeGen(unsureYet, definitelyFalse);
return;
case EQUAL: cmpsuffix = "eq"; break; writer.printf("%s:", unsureYet);
case NOTEQUAL: cmpsuffix = "ne"; break; this.right.codeGen(definitelyTrue, definitelyFalse);
case LESS: cmpsuffix = "lt"; break; }
case LESSEQUAL: cmpsuffix = "le"; break;
case GREATER: cmpsuffix = "gt"; break; void genShortCircuitOr(String definitelyTrue, String definitelyFalse) {
case GREATEREQUAL: cmpsuffix = "ge"; break; // label to jump to if right side must also be evaluated
} String unsureYet = makeLabel();
while
Negation
Kein zusätzlicher Bytecode-Befehl für logisch negierte Bedingungen, sondern Vertauschung while (x<10) {
der Sprungziele. Andere Strategien möglich, z. B. unter Verwendung “komplementärer” A
Vergleiche, z. B. if_icmpge statt if_icmplt für !(x<y) . }