From f98300fbe9bb29eb3eb2182b12094974a6dc195b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 12 Mar 2015 15:35:13 +0100 Subject: Update Programming Examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language cleaned up by the technical writers xsipewe and tmanevik from Combitech. Proofreading and corrections by Björn Gustavsson. --- system/doc/programming_examples/bit_syntax.xml | 210 ++++++++-------- system/doc/programming_examples/funs.xmlsrc | 280 +++++++++++---------- .../programming_examples/list_comprehensions.xml | 86 ++++--- system/doc/programming_examples/part.xml | 5 +- system/doc/programming_examples/records.xml | 88 ++++--- 5 files changed, 349 insertions(+), 320 deletions(-) diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml index fb321c1ba9..7ede5b71f9 100644 --- a/system/doc/programming_examples/bit_syntax.xml +++ b/system/doc/programming_examples/bit_syntax.xml @@ -31,62 +31,64 @@
Introduction -

In Erlang a Bin is used for constructing binaries and matching +

In Erlang, a Bin is used for constructing binaries and matching binary patterns. A Bin is written with the following syntax:

>]]> -

A Bin is a low-level sequence of bits or bytes. The purpose of a Bin is - to be able to, from a high level, construct a binary,

+

A Bin is a low-level sequence of bits or bytes. + The purpose of a Bin is to enable construction of binaries:

>]]> -

in which case all elements must be bound, or to match a binary,

+

All elements must be bound. Or match a binary:

> = Bin ]]> -

where Bin is bound, and where the elements are bound or +

Here, Bin is bound and the elements are bound or unbound, as in any match.

-

In R12B, a Bin need not consist of a whole number of bytes.

+

Since Erlang R12B, a Bin does not need to consist of a whole number of bytes.

A bitstring is a sequence of zero or more bits, where - the number of bits doesn't need to be divisible by 8. If the number + the number of bits does not need to be divisible by 8. If the number of bits is divisible by 8, the bitstring is also a binary.

Each element specifies a certain segment of the bitstring. A segment is a set of contiguous bits of the binary (not necessarily on a byte boundary). The first element specifies the initial segment, the second element specifies the following - segment etc.

-

The following examples illustrate how binaries are constructed + segment, and so on.

+

The following examples illustrate how binaries are constructed, or matched, and how elements and tails are specified.

Examples -

Example 1: A binary can be constructed from a set of +

Example 1: A binary can be constructed from a set of constants or a string literal:

>, Bin12 = <<"abc">>]]> -

yields binaries of size 3; binary_to_list(Bin11) - evaluates to [1, 17, 42], and - binary_to_list(Bin12) evaluates to [97, 98, 99].

-

Example 2: Similarly, a binary can be constructed +

This gives two binaries of size 3, with the following evaluations:

+ + binary_to_list(Bin11) evaluates to [1, 17, 42]. + binary_to_list(Bin12) evaluates to [97, 98, 99]. + +

Example 2:Similarly, a binary can be constructed from a set of bound variables:

>]]> -

yields a binary of size 4, and binary_to_list(Bin2) - evaluates to [1, 17, 00, 42] too. Here we used a - size expression for the variable C in order to +

This gives a binary of size 4. + Here, a size expression is used for the variable C to specify a 16-bits segment of Bin2.

-

Example 3: A Bin can also be used for matching: if +

binary_to_list(Bin2) evaluates to [1, 17, 00, 42].

+

Example 3: A Bin can also be used for matching. D, E, and F are unbound variables, and - Bin2 is bound as in the former example,

+ Bin2 is bound, as in Example 2:

> = Bin2]]> -

yields D = 273, E = 00, and F binds to a binary +

This gives D = 273, E = 00, and F binds to a binary of size 1: binary_to_list(F) = [42].

Example 4: The following is a more elaborate example - of matching, where Dgram is bound to the consecutive - bytes of an IP datagram of IP protocol version 4, and where we - want to extract the header and the data of the datagram:

+ of matching. Here, Dgram is bound to the consecutive + bytes of an IP datagram of IP protocol version 4. The ambition is + to extract the header and the data of the datagram:

> = RestDgram, ... end.]]> -

Here the segment corresponding to the Opts variable - has a type modifier specifying that Opts should +

Here, the segment corresponding to the Opts variable + has a type modifier, specifying that Opts is to bind to a binary. All other variables have the default type equal to unsigned integer.

-

An IP datagram header is of variable length, and its length - - measured in the number of 32-bit words - is given in - the segment corresponding to HLen, the minimum value of - which is 5. It is the segment corresponding to Opts - that is variable: if HLen is equal to 5, Opts - will be an empty binary.

+

An IP datagram header is of variable length. This length is + measured in the number of 32-bit words and is given in + the segment corresponding to HLen. The minimum value of + HLen is 5. It is the segment corresponding to Opts + that is variable, so if HLen is equal to 5, Opts + becomes an empty binary.

The tail variables RestDgram and Data bind to - binaries, as all tail variables do. Both may bind to empty + binaries, as all tail variables do. Both can bind to empty binaries.

-

If the first 4-bits segment of Dgram is not equal to - 4, or if HLen is less than 5, or if the size of - Dgram is less than 4*HLen, the match of - Dgram fails.

+

The match of Dgram fails if one of the following occurs:

+ + The first 4-bits segment of Dgram is not equal to 4. + HLen is less than 5. + The size of Dgram is less than 4*HLen. +
- A Lexical Note -

Note that ">]]>" will be interpreted as + Lexical Note +

Notice that ">]]>" will be interpreted as ">]]>", which is a syntax error. - The correct way to write the expression is - ">]]>".

+ The correct way to write the expression is: + >]]>.

Segments

Each segment has the following general syntax:

Value:Size/TypeSpecifierList

-

Both the Size and the TypeSpecifier or both may be - omitted; thus the following variations are allowed:

-

Value

-

Value:Size

-

Value/TypeSpecifierList

-

Default values will be used for missing specifications. - The default values are described in the section +

The Size or the TypeSpecifier, or both, can be + omitted. Thus, the following variants are allowed:

+ + Value + Value:Size + Value/TypeSpecifierList + +

Default values are used when specifications are missing. + The default values are described in Defaults.

-

Used in binary construction, the Value part is any - expression. Used in binary matching, the Value part must - be a literal or variable. You can read more about - the Value part in the section about constructing - binaries and matching binaries.

+

The Value part is any expression, when used in binary construction. + Used in binary matching, the Value part must + be a literal or a variable. For more information about + the Value part, see + Constructing Binaries and Bitstrings + and + Matching Binaries.

The Size part of the segment multiplied by the unit in - the TypeSpecifierList (described below) gives the number + TypeSpecifierList (described later) gives the number of bits for the segment. In construction, Size is any expression that evaluates to an integer. In matching, Size must be a constant expression or a variable.

@@ -160,22 +168,22 @@ end.]]> binary. Signedness The signedness specification can be either signed - or unsigned. Note that signedness only matters for + or unsigned. Notice that signedness only matters for matching. Endianness The endianness specification can be either big, little, or native. Native-endian means that - the endian will be resolved at load time to be either + the endian is resolved at load time, to be either big-endian or little-endian, depending on what is "native" for the CPU that the Erlang machine is run on. Unit The unit size is given as unit:IntegerLiteral. - The allowed range is 1-256. It will be multiplied by + The allowed range is 1-256. It is multiplied by the Size specifier to give the effective size of - the segment. In R12B, the unit size specifies the alignment - for binary segments without size (examples will follow). + the segment. Since Erlang R12B, the unit size specifies the alignment + for binary segments without size. -

Example:

+

Example:

X:4/little-signed-integer-unit:8

This element has a total size of 4*8 = 32 bits, and it contains @@ -184,13 +192,14 @@ X:4/little-signed-integer-unit:8

Defaults -

The default type for a segment is integer. The default +

The default type for + a segment is integer. The default type does not depend on the value, even if the value is a - literal. For instance, the default type in '>]]>' is + literal. For example, the default type in >]]> is integer, not float.

The default Size depends on the type. For integer it is 8. For float it is 64. For binary it is all of the binary. In - matching, this default value is only valid for the very last + matching, this default value is only valid for the last element. All other binary elements in matching must have a size specification.

The default unit depends on the the type. For integer, @@ -201,61 +210,60 @@ X:4/little-signed-integer-unit:8

Constructing Binaries and Bitstrings +

This section describes the rules for constructing binaries using the bit syntax. Unlike when constructing lists or tuples, the construction of a binary can fail with a badarg exception.

There can be zero or more segments in a binary to be - constructed. The expression '>]]>' constructs a zero + constructed. The expression >]]> constructs a zero length binary.

Each segment in a binary can consist of zero or more bits. There are no alignment rules for individual segments of type integer and float. For binaries and bitstrings without size, the unit specifies the alignment. Since the default alignment for the binary type is 8, the size of a binary - segment must be a multiple of 8 bits (i.e. only whole bytes). - Example:

+ segment must be a multiple of 8 bits, that is, only whole bytes.

+

Example:

>]]>

The variable Bin must contain a whole number of bytes, because the binary type defaults to unit:8. - A badarg exception will be generated if Bin would - consist of (for instance) 17 bits.

+ A badarg exception is generated if Bin + consist of, for example, 17 bits.

-

On the other hand, the variable Bitstring may consist of - any number of bits, for instance 0, 1, 8, 11, 17, 42, and so on, - because the default unit for bitstrings is 1.

+

The Bitstring variable can consist of + any number of bits, for example, 0, 1, 8, 11, 17, 42, and so on. + This is because the default unit for bitstrings is 1.

-

For clarity, it is recommended not to change the unit - size for binaries, but to use binary when you need byte - alignment, and bitstring when you need bit alignment.

+

For clarity, it is recommended not to change the unit + size for binaries. Instead, use binary when you need byte alignment + and bitstring when you need bit alignment.

-

The following example

+

The following example successfully constructs a bitstring of 7 bits, + provided that all of X and Y are integers:

>]]> -

will successfully construct a bitstring of 7 bits. - (Provided that all of X and Y are integers.)

-

As noted earlier, segments have the following general syntax:

+

As mentioned earlier, segments have the following general syntax:

Value:Size/TypeSpecifierList

When constructing binaries, Value and Size can be any Erlang expression. However, for syntactical reasons, both Value and Size must be enclosed in parenthesis if the expression consists of anything more than a single literal - or variable. The following gives a compiler syntax error:

+ or a variable. The following gives a compiler syntax error:

>]]> -

This expression must be rewritten to

+

This expression must be rewritten into the following, + to be accepted by the compiler:

>]]> -

in order to be accepted by the compiler.

Including Literal Strings -

As syntactic sugar, an literal string may be written instead - of a element.

+

A literal string can be written instead of an element:

>]]> -

which is syntactic sugar for

+

This is syntactic sugar for the following:

>]]>
@@ -263,29 +271,30 @@ X:4/little-signed-integer-unit:8
Matching Binaries -

This section describes the rules for matching binaries using + +

This section describes the rules for matching binaries, using the bit syntax.

There can be zero or more segments in a binary pattern. - A binary pattern can occur in every place patterns are allowed, - also inside other patterns. Binary patterns cannot be nested.

-

The pattern '>]]>' matches a zero length binary.

-

Each segment in a binary can consist of zero or more bits.

-

A segment of type binary must have a size evenly - divisible by 8 (or divisible by the unit size, if the unit size has been changed).

-

A segment of type bitstring has no restrictions on the size.

-

As noted earlier, segments have the following general syntax:

+ A binary pattern can occur wherever patterns are allowed, + including inside other patterns. Binary patterns cannot be nested. + The pattern >]]> matches a zero length binary.

+

Each segment in a binary can consist of zero or more bits. + A segment of type binary must have a size evenly divisible by 8 + (or divisible by the unit size, if the unit size has been changed). + A segment of type bitstring has no restrictions on the size.

+

As mentioned earlier, segments have the following general syntax:

Value:Size/TypeSpecifierList

-

When matching Value value must be either a variable or - an integer or floating point literal. Expressions are not +

When matching Value, value must be either a variable or + an integer, or a floating point literal. Expressions are not allowed.

Size must be an integer literal, or a previously bound - variable. Note that the following is not allowed:

+ variable. The following is not allowed:

>) -> {X,T}.]]>

The two occurrences of N are not related. The compiler will complain that the N in the size field is unbound.

-

The correct way to write this example is like this:

+

The correct way to write this example is as follows:

<> = Bin, @@ -303,14 +312,14 @@ foo(<>) ->]]> without size:

>) ->]]> -

There is no restriction on the number of bits in the tail.

+

There are no restrictions on the number of bits in the tail.

Appending to a Binary -

In R12B, the following function for creating a binary out of - a list of triples of integers is now efficient:

+

Since Erlang R12B, the following function for creating a binary out of + a list of triples of integers is efficient:

triples_to_bin(T, <<>>). @@ -321,7 +330,8 @@ triples_to_bin([], Acc) -> Acc.]]>

In previous releases, this function was highly inefficient, because the binary constructed so far (Acc) was copied in each recursion step. - That is no longer the case. See the Efficiency Guide for more information.

+ That is no longer the case. For more information, see + Efficiency Guide.

diff --git a/system/doc/programming_examples/funs.xmlsrc b/system/doc/programming_examples/funs.xmlsrc index 7bfac9db8c..e4f5c9c9c9 100644 --- a/system/doc/programming_examples/funs.xmlsrc +++ b/system/doc/programming_examples/funs.xmlsrc @@ -30,128 +30,124 @@
- Example 1 - map -

If we want to double every element in a list, we could write a - function named double:

+ map +

The following function, double, doubles every element in a list:

double([H|T]) -> [2*H|double(T)]; double([]) -> []. -

This function obviously doubles the argument entered as input - as follows:

+

Hence, the argument entered as input is doubled as follows:

 > double([1,2,3,4]).
 [2,4,6,8]
-

We now add the function add_one, which adds one to every +

The following function, add_one, adds one to every element in a list:

add_one([H|T]) -> [H+1|add_one(T)]; add_one([]) -> []. -

These functions, double and add_one, have a very - similar structure. We can exploit this fact and write a function - map which expresses this similarity:

+

The functions double and add_one have a + similar structure. This can be used by writing a function + map that expresses this similarity:

-

We can now express the functions double and - add_one in terms of map as follows:

+

The functions double and add_one can now be expressed + in terms of map as follows:

double(L) -> map(fun(X) -> 2*X end, L). add_one(L) -> map(fun(X) -> 1 + X end, L). -

map(F, List) is a function which takes a function - F and a list L as arguments and returns the new - list which is obtained by applying F to each of +

map(F, List) is a function that takes a function + F and a list L as arguments and returns a new + list, obtained by applying F to each of the elements in L.

The process of abstracting out the common features of a number - of different programs is called procedural abstraction. - Procedural abstraction can be used in order to write several - different functions which have a similar structure, but differ - only in some minor detail. This is done as follows:

+ of different programs is called procedural abstraction. + Procedural abstraction can be used to write several + different functions that have a similar structure, but differ + in some minor detail. This is done as follows:

- write one function which represents the common features of - these functions - parameterize the difference in terms of functions which + Step 1. Write one function that represents the common features of + these functions. + Step 2. Parameterize the difference in terms of functions that are passed as arguments to the common function.
- Example 2 - foreach -

This example illustrates procedural abstraction. Initially, we - show the following two examples written as conventional - functions:

- - all elements of a list are printed onto a stream - a message is broadcast to a list of processes. - + foreach +

This section illustrates procedural abstraction. Initially, + the following two examples are written as conventional + functions.

+

This function prints all elements of a list onto a stream:

print_list(Stream, [H|T]) -> io:format(Stream, "~p~n", [H]), print_list(Stream, T); print_list(Stream, []) -> true. +

This function broadcasts a message to a list of processes:

broadcast(Msg, [Pid|Pids]) -> Pid ! Msg, broadcast(Msg, Pids); broadcast(_, []) -> true. -

Both these functions have a very similar structure. They both - iterate over a list doing something to each element in the list. - The "something" has to be carried round as an extra argument to - the function which does this.

+

These two functions have a similar structure. They both + iterate over a list and do something to each element in the list. + The "something" is passed on as an extra argument to + the function that does this.

The function foreach expresses this similarity:

-

Using foreach, print_list becomes:

+

Using the function foreach, the function print_list becomes:

foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L) -

broadcast becomes:

+

Using the function foreach, the function broadcast becomes:

foreach(fun(Pid) -> Pid ! M end, L)

foreach is evaluated for its side-effect and not its value. foreach(Fun ,L) calls Fun(X) for each element X in L and the processing occurs in - the order in which the elements were defined in L. + the order that the elements were defined in L. map does not define the order in which its elements are processed.

- The Syntax of Funs -

Funs are written with the syntax:

+ Syntax of Funs +

Funs are written with the following syntax:

F = fun (Arg1, Arg2, ... ArgN) -> ... end

This creates an anonymous function of N arguments and binds it to the variable F.

-

If we have already written a function in the same module and - wish to pass this function as an argument, we can use - the following syntax:

+

Another function, FunctionName, written in the same module, + can be passed as an argument, using the following syntax:

F = fun FunctionName/Arity -

With this form of function reference, the function which is +

With this form of function reference, the function that is referred to does not need to be exported from the module.

-

We can also refer to a function defined in a different module +

It is also possible to refer to a function defined in a different module, with the following syntax:

F = {Module, FunctionName}

In this case, the function must be exported from the module in question.

-

The follow program illustrates the different ways of creating +

The following program illustrates the different ways of creating funs:

-

We can evaluate the fun F with the syntax:

+

The fun F can be evaluated with the following syntax:

F(Arg1, Arg2, ..., Argn)

To check whether a term is a fun, use the test - is_function/1 in a guard. Example:

+ is_function/1 in a guard.

+

Example:

f(F, Args) when is_function(F) -> apply(F, Args); f(N, _) when is_integer(N) -> N. -

Funs are a distinct type. The BIFs erlang:fun_info/1,2 can +

Funs are a distinct type. The BIFs erlang:fun_info/1,2 can be used to retrieve information about a fun, and the BIF - erlang:fun_to_list/1 returns a textual representation of a fun. - The check_process_code/2 BIF returns true if the process + erlang:fun_to_list/1 returns a textual representation of a fun. + The check_process_code/2 BIF returns true if the process contains funs that depend on the old version of a module.

In OTP R5 and earlier releases, funs were represented using @@ -161,15 +157,15 @@ f(N, _) when is_integer(N) ->

Variable Bindings Within a Fun -

The scope rules for variables which occur in funs are as +

The scope rules for variables that occur in funs are as follows:

- All variables which occur in the head of a fun are assumed + All variables that occur in the head of a fun are assumed to be "fresh" variables. - Variables which are defined before the fun, and which + Variables that are defined before the fun, and that occur in function calls or guard tests within the fun, have the values they had outside the fun. - No variables may be exported from a fun. + Variables cannot be exported from a fun.

The following examples illustrate these rules:

@@ -177,12 +173,13 @@ print_list(File, List) -> {ok, Stream} = file:open(File, write), foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List), file:close(Stream). -

In the above example, the variable X which is defined in - the head of the fun is a new variable. The value of the variable - Stream which is used within within the fun gets its value +

Here, the variable X, defined in + the head of the fun, is a new variable. The variable + Stream, which is used within the fun, gets its value from the file:open line.

-

Since any variable which occurs in the head of a fun is - considered a new variable it would be equally valid to write:

+

As any variable that occurs in the head of a fun is + considered a new variable, it is equally valid to write + as follows:

print_list(File, List) -> {ok, Stream} = file:open(File, write), @@ -190,21 +187,21 @@ print_list(File, List) -> io:format(Stream,"~p~n",[File]) end, List), file:close(Stream). -

In this example, File is used as the new variable - instead of X. This is rather silly since code in the body - of the fun cannot refer to the variable File which is - defined outside the fun. Compiling this example will yield - the diagnostic:

+

Here, File is used as the new variable + instead of X. This is not so wise because code in the fun + body cannot refer to the variable File, which is + defined outside of the fun. Compiling this example gives + the following diagnostic:

./FileName.erl:Line: Warning: variable 'File' shadowed in 'lambda head' -

This reminds us that the variable File which is defined - inside the fun collides with the variable File which is +

This indicates that the variable File, which is defined + inside the fun, collides with the variable File, which is defined outside the fun.

The rules for importing variables into a fun has the consequence - that certain pattern matching operations have to be moved into + that certain pattern matching operations must be moved into guard expressions and cannot be written in the head of the fun. - For example, we might write the following code if we intend + For example, you might write the following code if you intend the first clause of F to be evaluated when the value of its argument is Y:

@@ -216,7 +213,7 @@ f(...) -> ... end, ...) ... -

instead of

+

instead of writng the following code:

f(...) -> Y = ... @@ -229,35 +226,37 @@ f(...) ->
- Funs and the Module Lists + Funs and Module Lists

The following examples show a dialogue with the Erlang shell. All the higher order functions discussed are exported from the module lists.

map +

map takes a function of one argument and a list of terms:

-

map takes a function of one argument and a list of - terms. It returns the list obtained by applying the function +

It returns the list obtained by applying the function to every argument in the list.

+

When a new fun is defined in the shell, the value of the fun + is printed as ]]>:

 > Double = fun(X) -> 2 * X end.
 #Fun<erl_eval.6.72228031>
 > lists:map(Double, [1,2,3,4,5]).
 [2,4,6,8,10]
-

When a new fun is defined in the shell, the value of the Fun - is printed as ]]>.

+
any -

any takes a predicate P of one argument and a - list of terms. A predicate is a function which returns - true or false. any is true if there is a - term X in the list such that P(X) is true.

-

We define a predicate Big(X) which is true if - its argument is greater that 10.

+ list of terms:

+ +

A predicate is a function that returns true or false. + any is true if there is a term X in the list such that + P(X) is true.

+

A predicate Big(X) is defined, which is true if + its argument is greater that 10:

 > Big =  fun(X) -> if X > 10 -> true; true -> false end end.
 #Fun<erl_eval.6.72228031>
@@ -269,9 +268,10 @@ true
all +

all has the same arguments as any:

-

all has the same arguments as any. It is true - if the predicate applied to all elements in the list is true.

+

It is true + if the predicate applied to all elements in the list is true.

 > lists:all(Big, [1,2,3,4,12,6]).   
 false
@@ -281,11 +281,12 @@ true
foreach -

foreach takes a function of one argument and a list of - terms. The function is applied to each argument in the list. - foreach returns ok. It is used for its - side-effect only.

+ terms:

+ +

The function is applied to each argument in the list. + foreach returns ok. It is only used for its + side-effect:

 > lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]). 
 1
@@ -297,15 +298,16 @@ ok
foldl -

foldl takes a function of two arguments, an - accumulator and a list. The function is called with two + accumulator and a list:

+ +

The function is called with two arguments. The first argument is the successive elements in - the list, the second argument is the accumulator. The function - must return a new accumulator which is used the next time + the list. The second argument is the accumulator. The function + must return a new accumulator, which is used the next time the function is called.

-

If we have a list of lists L = ["I","like","Erlang"], - then we can sum the lengths of all the strings in L as +

If you have a list of lists L = ["I","like","Erlang"], + then you can sum the lengths of all the strings in L as follows:

 > L = ["I","like","Erlang"].
@@ -325,11 +327,11 @@ end
 
     
mapfoldl +

mapfoldl simultaneously maps and folds over a list:

-

mapfoldl simultaneously maps and folds over a list. - The following example shows how to change all letters in - L to upper case and count them.

-

First upcase:

+

The following example shows how to change all letters in + L to upper case and then count them.

+

First the change to upper case:

 > Upcase =  fun(X) when $a =< X,  X =< $z -> X + $A - $a;
 (X) -> X 
@@ -344,7 +346,7 @@ end
 "ERLANG"
 > lists:map(Upcase_word, L).
 ["I","LIKE","ERLANG"]
-

Now we can do the fold and the map at the same time:

+

Now, the fold and the map can be done at the same time:

 > lists:mapfoldl(fun(Word, Sum) ->
 {Upcase_word(Word), Sum + length(Word)}
@@ -354,23 +356,24 @@ end
 
     
filter -

filter takes a predicate of one argument and a list - and returns all element in the list which satisfy - the predicate.

+ and returns all elements in the list that satisfy + the predicate:

+
 > lists:filter(Big, [500,12,2,45,6,7]).
 [500,12,45]
-

When we combine maps and filters we can write very succinct - code. For example, suppose we want to define a set difference - function. We want to define diff(L1, L2) to be - the difference between the lists L1 and L2. - This is the list of all elements in L1 which are not contained - in L2. This code can be written as follows:

+

Combining maps and filters enables writing of very succinct + code. For example, to define a set difference + function diff(L1, L2) to be + the difference between the lists L1 and L2, + the code can be written as follows:

diff(L1, L2) -> filter(fun(X) -> not member(X, L2) end, L1). -

The AND intersection of the list L1 and L2 is +

This gives the list of all elements in L1 that are not contained + in L2.

+

The AND intersection of the list L1 and L2 is also easily defined:

intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2). @@ -378,9 +381,9 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
takewhile -

takewhile(P, L) takes elements X from a list - L as long as the predicate P(X) is true.

+ L as long as the predicate P(X) is true:

+
 > lists:takewhile(Big, [200,500,45,5,3,45,6]).  
 [200,500,45]
@@ -388,8 +391,8 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
dropwhile +

dropwhile is the complement of takewhile:

-

dropwhile is the complement of takewhile.

 > lists:dropwhile(Big, [200,500,45,5,3,45,6]).
 [5,3,45,6]
@@ -397,10 +400,10 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
splitwith -

splitwith(P, L) splits the list L into the two - sub-lists {L1, L2}, where L = takewhile(P, L) - and L2 = dropwhile(P, L).

+ sublists {L1, L2}, where L = takewhile(P, L) + and L2 = dropwhile(P, L):

+
 > lists:splitwith(Big, [200,500,45,5,3,45,6]).
 {[200,500,45],[5,3,45,6]}
@@ -408,17 +411,17 @@ intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
- Funs Which Return Funs -

So far, this section has only described functions which take - funs as arguments. It is also possible to write more powerful - functions which themselves return funs. The following examples - illustrate these type of functions.

+ Funs Returning Funs +

So far, only functions that take + funs as arguments have been described. More powerful + functions, that themselves return funs, can also be written. The following + examples illustrate these type of functions.

Simple Higher Order Functions -

Adder(X) is a function which, given X, returns +

Adder(X) is a function that given X, returns a new function G such that G(K) returns - K + X.

+ K + X:

 > Adder = fun(X) -> fun(Y) -> X + Y end end.
 #Fun<erl_eval.6.72228031>
@@ -438,7 +441,7 @@ ints_from(N) ->
     fun() ->
             [N|ints_from(N+1)]
     end.
-      

Then we can proceed as follows:

+

Then proceed as follows:

 > XX = lazy:ints_from(1).
 #Fun<lazy.0.29874839>
@@ -450,7 +453,7 @@ ints_from(N) ->
 #Fun<lazy.0.29874839>
 > hd(Y()).
 2
-

etc. - this is an example of "lazy embedding".

+

And so on. This is an example of "lazy embedding".

@@ -459,17 +462,21 @@ ints_from(N) ->
 Parser(Toks) -> {ok, Tree, Toks1} | fail

Toks is the list of tokens to be parsed. A successful - parse returns {ok, Tree, Toks1}, where Tree is a - parse tree and Toks1 is a tail of Tree which - contains symbols encountered after the structure which was - correctly parsed. Otherwise fail is returned.

-

The example which follows illustrates a simple, functional - parser which parses the grammar:

+ parse returns {ok, Tree, Toks1}.

+ + Tree is a parse tree. + Toks1 is a tail of Tree that + contains symbols encountered after the structure that was + correctly parsed. + +

An unsuccessful parse returns fail.

+

The following example illustrates a simple, functional + parser that parses the grammar:

 (a | b) & (c | d)

The following code defines a function pconst(X) in - the module funparse, which returns a fun which parses a - list of tokens.

+ the module funparse, which returns a fun that parses a + list of tokens:

This function can be used as follows:

@@ -479,17 +486,18 @@ Parser(Toks) -> {ok, Tree, Toks1} | fail
{ok,{const,a},[b,c]} > P1([x,y,z]). fail
-

Next, we define the two higher order functions pand - and por which combine primitive parsers to produce more - complex parsers. Firstly pand:

+

Next, the two higher order functions pand + and por are defined. They combine primitive parsers to produce more + complex parsers.

+

First pand:

Given a parser P1 for grammar G1, and a parser P2 for grammar G2, pand(P1, P2) returns a - parser for the grammar which consists of sequences of tokens - which satisfy G1 followed by sequences of tokens which + parser for the grammar, which consists of sequences of tokens + that satisfy G1, followed by sequences of tokens that satisfy G2.

por(P1, P2) returns a parser for the language - described by the grammar G1 or G2.

+ described by the grammar G1 or G2:

The original problem was to parse the grammar . The following code addresses this @@ -497,7 +505,7 @@ fail

The following code adds a parser interface to the grammar:

-

We can test this parser as follows:

+

The parser can be tested as follows:

 > funparse:parse([a,c]).
 {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}}
diff --git a/system/doc/programming_examples/list_comprehensions.xml b/system/doc/programming_examples/list_comprehensions.xml
index d6c8a66e13..5b33b14dea 100644
--- a/system/doc/programming_examples/list_comprehensions.xml
+++ b/system/doc/programming_examples/list_comprehensions.xml
@@ -31,18 +31,15 @@
 
   
Simple Examples -

We start with a simple example:

+

This section starts with a simple example, showing a generator and a filter:

 > [X || X <- [1,2,a,3,4,b,5,6], X > 3].
 [a,4,b,5,6]
-

This should be read as follows:

- -

The list of X such that X is taken from the list +

This is read as follows: The list of X such that X is taken from the list [1,2,a,...] and X is greater than 3.

-

The notation is a generator and the expression X > 3 is a filter.

-

An additional filter can be added in order to restrict +

An additional filter, integer(X), can be added to restrict the result to integers:

 > [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].
@@ -56,7 +53,7 @@
 
   
Quick Sort -

The well known quick sort routine can be written as follows:

+

The well-known quick sort routine can be written as follows:

sort([ X || X <- T, X < Pivot]) ++ @@ -64,15 +61,20 @@ sort([Pivot|T]) -> sort([ X || X <- T, X >= Pivot]); sort([]) -> [].]]>

The expression is the list of - all elements in T, which are less than Pivot.

+ all elements in T that are less than Pivot.

= Pivot]]]> is the list of all elements in - T, which are greater or equal to Pivot.

-

To sort a list, we isolate the first element in the list and - split the list into two sub-lists. The first sub-list contains - all elements which are smaller than the first element in - the list, the second contains all elements which are greater - than or equal to the first element in the list. We then sort - the sub-lists and combine the results.

+ T that are greater than or equal to Pivot.

+

A list sorted as follows:

+ + The first element in the list is isolated + and the list is split into two sublists. + The first sublist contains + all elements that are smaller than the first element in + the list. + The second sublist contains all elements that are greater + than, or equal to, the first element in the list. + Then the sublists are sorted and the results are combined. +
@@ -82,10 +84,10 @@ sort([]) -> [].]]> [[]]; perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].]]> -

We take take H from L in all possible ways. +

This takes H from L in all possible ways. The result is the set of all lists [H|T], where T - is the set of all possible permutations of L with - H removed.

+ is the set of all possible permutations of L, with + H removed:

 > perms([b,u,g]).
 [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]]
@@ -97,7 +99,7 @@ perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].]]> that A**2 + B**2 = C**2.

The function pyth(N) generates a list of all integers {A,B,C} such that A**2 + B**2 = C**2 and where - the sum of the sides is equal to or less than N.

+ the sum of the sides is equal to, or less than, N:

[ {A,B,C} || @@ -140,7 +142,7 @@ pyth1(N) ->
- Simplifications with List Comprehensions + Simplifications With List Comprehensions

As an example, list comprehensions can be used to simplify some of the functions in lists.erl:

[X || X <- L, Pred(X)].]]>
Variable Bindings in List Comprehensions -

The scope rules for variables which occur in list +

The scope rules for variables that occur in list comprehensions are as follows:

- all variables which occur in a generator pattern are - assumed to be "fresh" variables - any variables which are defined before the list - comprehension and which are used in filters have the values - they had before the list comprehension - no variables may be exported from a list comprehension. + All variables that occur in a generator pattern are + assumed to be "fresh" variables. + Any variables that are defined before the list + comprehension, and that are used in filters, have the values + they had before the list comprehension. + Variables cannot be exported from a list comprehension. -

As an example of these rules, suppose we want to write +

As an example of these rules, suppose you want to write the function select, which selects certain elements from - a list of tuples. We might write + a list of tuples. Suppose you write [Y || {X, Y} <- L].]]> with the intention - of extracting all tuples from L where the first item is + of extracting all tuples from L, where the first item is X.

-

Compiling this yields the following diagnostic:

+

Compiling this gives the following diagnostic:

./FileName.erl:Line: Warning: variable 'X' shadowed in generate -

This diagnostic warns us that the variable X in - the pattern is not the same variable as the variable X - which occurs in the function head.

-

Evaluating select yields the following result:

+

This diagnostic warns that the variable X in + the pattern is not the same as the variable X + that occurs in the function head.

+

Evaluating select gives the following result:

 > select(b,[{a,1},{b,2},{c,3},{b,7}]).
 [1,2,3,7]
-

This result is not what we wanted. To achieve the desired - effect we must write select as follows:

+

This is not the wanted result. To achieve the desired + effect, select must be written as follows:

[Y || {X1, Y} <- L, X == X1].]]>

The generator now contains unbound variables and the test has - been moved into the filter. This now works as expected:

+ been moved into the filter.

+

This now works as expected:

 > select(b,[{a,1},{b,2},{c,3},{b,7}]).
 [2,7]
-

One consequence of the rules for importing variables into a +

A consequence of the rules for importing variables into a list comprehensions is that certain pattern matching operations - have to be moved into the filters and cannot be written directly - in the generators. To illustrate this, do not write as follows:

+ must be moved into the filters and cannot be written directly + in the generators.

+

To illustrate this, do not write as follows:

Y = ... diff --git a/system/doc/programming_examples/part.xml b/system/doc/programming_examples/part.xml index 0bec9b4cf5..9329717ce4 100644 --- a/system/doc/programming_examples/part.xml +++ b/system/doc/programming_examples/part.xml @@ -28,8 +28,9 @@ -

This chapter contains examples on using records, funs, list - comprehensions and the bit syntax.

+ +

This section contains examples on using records, funs, list + comprehensions, and the bit syntax.

diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml index 58cf136a0b..ffcc05e758 100644 --- a/system/doc/programming_examples/records.xml +++ b/system/doc/programming_examples/records.xml @@ -30,37 +30,39 @@
- Records vs Tuples -

The main advantage of using records instead of tuples is that + Records and Tuples +

The main advantage of using records rather than tuples is that fields in a record are accessed by name, whereas fields in a tuple are accessed by position. To illustrate these differences, - suppose that we want to represent a person with the tuple + suppose that you want to represent a person with the tuple {Name, Address, Phone}.

-

We must remember that the Name field is the first - element of the tuple, the Address field is the second - element, and so on, in order to write functions which manipulate - this data. For example, to extract data from a variable P - which contains such a tuple we might write the following code - and then use pattern matching to extract the relevant fields.

+

To write functions that manipulate this data, remember the following:

+ + The Name field is the first element of the tuple. + The Address field is the second element. + The Phone field is the third element. + +

For example, to extract data from a variable P + that contains such a tuple, you can write the following code + and then use pattern matching to extract the relevant fields:

Name = element(1, P), Address = element(2, P), ... -

Code like this is difficult to read and understand and errors - occur if we get the numbering of the elements in the tuple wrong. - If we change the data representation by re-ordering the fields, - or by adding or removing a field, then all references to - the person tuple, wherever they occur, must be checked and - possibly modified.

-

Records allow us to refer to the fields by name and not - position. We use a record instead of a tuple to store the data. - If we write a record definition of the type shown below, we can - then refer to the fields of the record by name.

+

Such code is difficult to read and understand, and errors + occur if the numbering of the elements in the tuple is wrong. + If the data representation of the fields is changed, by re-ordering, + adding, or removing fields, all references to + the person tuple must be checked and possibly modified.

+

Records allow references to the fields by name, instead of by + position. In the following example, a record instead of a tuple + is used to store the data:

-record(person, {name, phone, address}). -

For example, if P is now a variable whose value is a - person record, we can code as follows in order to access - the name and address fields of the records.

+

This enables references to the fields of the record by name. + For example, if P is a variable whose value is a + person record, the following code access + the name and address fields of the records:

Name = P#person.name, Address = P#person.address, @@ -72,24 +74,25 @@ Address = P#person.address,
Defining a Record -

This definition of a person will be used in many of - the examples which follow. It contains three fields, name, - phone and address. The default values for +

This following definition of a person is used in several + examples in this section. Three fields are included, name, + phone, and address. The default values for name and phone is "" and [], respectively. The default value for address is the atom undefined, since no default value is supplied for this field:

 -record(person, {name = "", phone = [], address}).
-

We have to define the record in the shell in order to be able - use the record syntax in the examples:

+

The record must be defined in the shell to enable + use of the record syntax in the examples:

 > rd(person, {name = "", phone = [], address}).
 person
-

This is due to the fact that record definitions are available - at compile time only, not at runtime. See shell(3) for - details on records in the shell. -

+

This is because record definitions are only available + at compile time, not at runtime. For details on records + in the shell, see the + shell(3) + manual page in stdlib.

@@ -98,12 +101,12 @@ person
 > #person{phone=[0,8,2,3,4,3,1,2], name="Robert"}.
 #person{name = "Robert",phone = [0,8,2,3,4,3,1,2],address = undefined}
-

Since the address field was omitted, its default value +

As the address field was omitted, its default value is used.

-

There is a new feature introduced in Erlang 5.1/OTP R8B, - with which you can set a value to all fields in a record, - overriding the defaults in the record specification. The special - field _, means "all fields not explicitly specified".

+

From Erlang 5.1/OTP R8B, a value to all + fields in a record can be set with the special field _. + _ means "all fields not explicitly specified".

+

Example:

 > #person{name = "Jakob", _ = '_'}.
 #person{name = "Jakob",phone = '_',address = '_'}
@@ -114,6 +117,7 @@ person
Accessing a Record Field +

The following example shows how to access a record field:

 > P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.
 #person{name = "Joe",phone = [0,8,2,3,4,3,1,2],address = undefined}
@@ -123,6 +127,7 @@ person
Updating a Record +

The following example shows how to update a record:

 > P1 = #person{name="Joe", phone=[1,2,3], address="A street"}.
 #person{name = "Joe",phone = [1,2,3],address = "A street"}
@@ -133,7 +138,7 @@ person
Type Testing

The following example shows that the guard succeeds if - P is record of type person.

+ P is record of type person:

 foo(P) when is_record(P, person) -> a_person;
 foo(_) -> not_a_person.
@@ -141,7 +146,7 @@ foo(_) -> not_a_person.
Pattern Matching -

Matching can be used in combination with records as shown in +

Matching can be used in combination with records, as shown in the following example:

 > P3 = #person{name="Joe", phone=[0,0,7], address="A street"}.
@@ -163,7 +168,7 @@ find_phone([], Name) ->
 
   
Nested Records -

The value of a field in a record might be an instance of a +

The value of a field in a record can be an instance of a record. Retrieval of nested data can be done stepwise, or in a single step, as shown in the following example:

@@ -173,11 +178,12 @@ find_phone([], Name) ->
 demo() ->
   P = #person{name= #name{first="Robert",last="Virding"}, phone=123},
   First = (P#person.name)#name.first.
-

In this example, demo() evaluates to "Robert".

+

Here, demo() evaluates to "Robert".

- Example + A Longer Example +

Comments are embedded in the following example:

 %% File: person.hrl
 
-- 
cgit v1.2.3