diff options
Diffstat (limited to 'system/doc/programming_examples')
-rw-r--r-- | system/doc/programming_examples/Makefile | 98 | ||||
-rw-r--r-- | system/doc/programming_examples/bit_syntax.xml | 327 | ||||
-rw-r--r-- | system/doc/programming_examples/book.xml | 37 | ||||
-rw-r--r-- | system/doc/programming_examples/fun_test.erl | 17 | ||||
-rw-r--r-- | system/doc/programming_examples/funparse.erl | 74 | ||||
-rw-r--r-- | system/doc/programming_examples/funs.xmlsrc | 515 | ||||
-rw-r--r-- | system/doc/programming_examples/funs1.erl | 125 | ||||
-rw-r--r-- | system/doc/programming_examples/list_comprehensions.xml | 206 | ||||
-rw-r--r-- | system/doc/programming_examples/make.dep | 20 | ||||
-rw-r--r-- | system/doc/programming_examples/part.xml | 39 | ||||
-rw-r--r-- | system/doc/programming_examples/records.xml | 232 | ||||
-rw-r--r-- | system/doc/programming_examples/xmlfiles.mk | 23 |
12 files changed, 1713 insertions, 0 deletions
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile new file mode 100644 index 0000000000..73512c9654 --- /dev/null +++ b/system/doc/programming_examples/Makefile @@ -0,0 +1,98 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include $(ERL_TOP)/erts/vsn.mk +#VSN=$(SYSTEM_VSN) + +APPLICATION=otp-system-documentation +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/doc/programming_examples + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +XML_PART_FILES = part.xml + +include xmlfiles.mk + +XML_CHAPTER_FILES=$(PROG_EX_CHAPTER_FILES) + +TOPDOCDIR=.. + +BOOK_FILES = book.xml + +GIF_FILES = + +PS_FILES = + +XML_FILES = \ + $(BOOK_FILES) $(XML_CHAPTER_FILES) \ + $(XML_PART_FILES) +# ---------------------------------------------------- + +HTML_FILES = \ + $(XML_PART_FILES:%.xml=%.html) + +HTMLDIR = ../html/programming_examples + +HTML_UG_FILE = $(HTMLDIR)/users_guide.html + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +XML_FLAGS += +DVIPS_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +docs: html +local_docs: PDFDIR=../../pdf + +html: $(GIF_FILES) $(HTML_UG_FILE) + +debug opt: + +clean clean_docs: + rm -rf $(HTMLDIR) + rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f errs core *~ + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_docs_spec: docs + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ + $(RELSYSDIR) + +release_spec: + + + diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml new file mode 100644 index 0000000000..3306365c0e --- /dev/null +++ b/system/doc/programming_examples/bit_syntax.xml @@ -0,0 +1,327 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Bit Syntax</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>bit_syntax.xml</file> + </header> + + <section> + <title>Introduction</title> + <p>In Erlang a Bin is used for constructing binaries and matching + binary patterns. A Bin is written with the following syntax:</p> + <code type="none"><![CDATA[ + <<E1, E2, ... En>>]]></code> + <p>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,</p> + <code type="none"><![CDATA[ +Bin = <<E1, E2, ... En>>]]></code> + <p>in which case all elements must be bound, or to match a binary,</p> + <code type="none"><![CDATA[ +<<E1, E2, ... En>> = Bin ]]></code> + <p>where <c>Bin</c> is bound, and where the elements are bound or + unbound, as in any match.</p> + <p>In R12B, a Bin need not consist of a whole number of bytes.</p> + + <p>A <em>bitstring</em> is a sequence of zero or more bits, where + the number of bits doesn't need to be divisible by 8. If the number + of bits is divisible by 8, the bitstring is also a binary.</p> + <p>Each element specifies a certain <em>segment</em> 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.</p> + <p>The following examples illustrate how binaries are constructed + or matched, and how elements and tails are specified.</p> + + <section> + <title>Examples</title> + <p><em>Example 1: </em>A binary can be constructed from a set of + constants or a string literal:</p> + <code type="none"><![CDATA[ +Bin11 = <<1, 17, 42>>, +Bin12 = <<"abc">>]]></code> + <p>yields binaries of size 3; <c>binary_to_list(Bin11)</c> + evaluates to <c>[1, 17, 42]</c>, and + <c>binary_to_list(Bin12)</c> evaluates to <c>[97, 98, 99]</c>.</p> + <p><em>Example 2: </em>Similarly, a binary can be constructed + from a set of bound variables:</p> + <code type="none"><![CDATA[ +A = 1, B = 17, C = 42, +Bin2 = <<A, B, C:16>>]]></code> + <p>yields a binary of size 4, and <c>binary_to_list(Bin2)</c> + evaluates to <c>[1, 17, 00, 42]</c> too. Here we used a + <em>size expression</em> for the variable <c>C</c> in order to + specify a 16-bits segment of <c>Bin2</c>.</p> + <p><em>Example 3: </em>A Bin can also be used for matching: if + <c>D</c>, <c>E</c>, and <c>F</c> are unbound variables, and + <c>Bin2</c> is bound as in the former example,</p> + <code type="none"><![CDATA[ +<<D:16, E, F/binary>> = Bin2]]></code> + <p>yields <c>D = 273</c>, <c>E = 00</c>, and F binds to a binary + of size 1: <c>binary_to_list(F) = [42]</c>.</p> + <p><em>Example 4:</em> The following is a more elaborate example + of matching, where <c>Dgram</c> 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:</p> + <code type="none"><![CDATA[ +-define(IP_VERSION, 4). +-define(IP_MIN_HDR_LEN, 5). + +DgramSize = byte_size(Dgram), +case Dgram of + <<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16, + ID:16, Flgs:3, FragOff:13, + TTL:8, Proto:8, HdrChkSum:16, + SrcIP:32, + DestIP:32, RestDgram/binary>> when HLen>=5, 4*HLen=<DgramSize -> + OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), + <<Opts:OptsLen/binary,Data/binary>> = RestDgram, + ... +end.]]></code> + <p>Here the segment corresponding to the <c>Opts</c> variable + has a <em>type modifier</em> specifying that <c>Opts</c> should + bind to a binary. All other variables have the default type + equal to unsigned integer.</p> + <p>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 <c>HLen</c>, the minimum value of + which is 5. It is the segment corresponding to <c>Opts</c> + that is variable: if <c>HLen</c> is equal to 5, <c>Opts</c> + will be an empty binary.</p> + <p>The tail variables <c>RestDgram</c> and <c>Data</c> bind to + binaries, as all tail variables do. Both may bind to empty + binaries.</p> + <p>If the first 4-bits segment of <c>Dgram</c> is not equal to + 4, or if <c>HLen</c> is less than 5, or if the size of + <c>Dgram</c> is less than <c>4*HLen</c>, the match of + <c>Dgram</c> fails.</p> + </section> + </section> + + <section> + <title>A Lexical Note</title> + <p>Note that "<c><![CDATA[B=<<1>>]]></c>" will be interpreted as + "<c><![CDATA[B =< <1>>]]></c>", which is a syntax error. + The correct way to write the expression is + "<c><![CDATA[B = <<1>>]]></c>".</p> + </section> + + <section> + <title>Segments</title> + <p>Each segment has the following general syntax:</p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>Both the <c>Size</c> and the <c>TypeSpecifier</c> or both may be + omitted; thus the following variations are allowed:</p> + <p><c>Value</c></p> + <p><c>Value:Size</c></p> + <p><c>Value/TypeSpecifierList</c></p> + <p>Default values will be used for missing specifications. + The default values are described in the section + <seealso marker="#Defaults">Defaults</seealso>.</p> + <p>Used in binary construction, the <c>Value</c> part is any + expression. Used in binary matching, the <c>Value</c> part must + be a literal or variable. You can read more about + the <c>Value</c> part in the section about constructing + binaries and matching binaries.</p> + <p>The <c>Size</c> part of the segment multiplied by the unit in + the <c>TypeSpecifierList</c> (described below) gives the number + of bits for the segment. In construction, <c>Size</c> is any + expression that evaluates to an integer. In matching, + <c>Size</c> must be a constant expression or a variable.</p> + <p>The <c>TypeSpecifierList</c> is a list of type specifiers + separated by hyphens.</p> + <taglist> + <tag>Type</tag> + <item>The type can be <c>integer</c>, <c>float</c>, or + <c>binary</c>.</item> + <tag>Signedness</tag> + <item>The signedness specification can be either <c>signed</c> + or <c>unsigned</c>. Note that signedness only matters for + matching.</item> + <tag>Endianness</tag> + <item>The endianness specification can be either <c>big</c>, + <c>little</c>, or <c>native</c>. Native-endian means that + the endian will be 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.</item> + <tag>Unit</tag> + <item>The unit size is given as <c>unit:IntegerLiteral</c>. + The allowed range is 1-256. It will be multiplied by + the <c>Size</c> 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).</item> + </taglist> + <p>Example:</p> + <code type="none"> +X:4/little-signed-integer-unit:8</code> + <p>This element has a total size of 4*8 = 32 bits, and it contains + a signed integer in little-endian order.</p> + </section> + + <section> + <title>Defaults</title> + <p><marker id="Defaults"></marker>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 '<c><![CDATA[<<3.14>>]]></c>' is + integer, not float.</p> + <p>The default <c>Size</c> 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 + element. All other binary elements in matching must have a size + specification.</p> + <p>The default unit depends on the the type. For <c>integer</c>, + <c>float</c>, and <c>bitstring</c> it is 1. For binary it is 8.</p> + <p>The default signedness is <c>unsigned</c>.</p> + <p>The default endianness is <c>big</c>.</p> + </section> + + <section> + <title>Constructing Binaries and Bitstrings</title> + <p>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 <c>badarg</c> + exception.</p> + <p>There can be zero or more segments in a binary to be + constructed. The expression '<c><![CDATA[<<>>]]></c>' constructs a zero + length binary.</p> + <p>Each segment in a binary can consist of zero or more bits. + There are no alignment rules for individual segments of type + <c>integer</c> and <c>float</c>. For binaries and bitstrings + without size, the unit specifies the alignment. Since the default + alignment for the <c>binary</c> type is 8, the size of a binary + segment must be a multiple of 8 bits (i.e. only whole bytes). + Example:</p> + <code type="none"><![CDATA[ +<<Bin/binary,Bitstring/bitstring>>]]></code> + <p>The variable <c>Bin</c> must contain a whole number of bytes, + because the <c>binary</c> type defaults to <c>unit:8</c>. + A <c>badarg</c> exception will be generated if <c>Bin</c> would + consist of (for instance) 17 bits.</p> + + <p>On the other hand, the variable <c>Bitstring</c> may consist of + any number of bits, for instance 0, 1, 8, 11, 17, 42, and so on, + because the default <c>unit</c> for bitstrings is 1.</p> + + <warning><p>For clarity, it is recommended not to change the unit + size for binaries, but to use <c>binary</c> when you need byte + alignment, and <c>bitstring</c> when you need bit alignment.</p></warning> + + <p>The following example</p> + <code type="none"><![CDATA[ +<<X:1,Y:6>>]]></code> + <p>will successfully construct a bitstring of 7 bits. + (Provided that all of X and Y are integers.)</p> + <p>As noted earlier, segments have the following general syntax:</p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>When constructing binaries, <c>Value</c> and <c>Size</c> can be + any Erlang expression. However, for syntactical reasons, both + <c>Value</c> and <c>Size</c> 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:</p> + <code type="none"><![CDATA[ +<<X+1:8>>]]></code> + <p>This expression must be rewritten to</p> + <code type="none"><![CDATA[ +<<(X+1):8>>]]></code> + <p>in order to be accepted by the compiler.</p> + + <section> + <title>Including Literal Strings</title> + <p>As syntactic sugar, an literal string may be written instead + of a element.</p> + <code type="none"><![CDATA[ +<<"hello">>]]></code> + <p>which is syntactic sugar for</p> + <code type="none"><![CDATA[ +<<$h,$e,$l,$l,$o>>]]></code> + </section> + </section> + + <section> + <title>Matching Binaries</title> + <p>This section describes the rules for matching binaries using + the bit syntax.</p> + <p>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.</p> + <p>The pattern '<c><![CDATA[<<>>]]></c>' matches a zero length binary.</p> + <p>Each segment in a binary can consist of zero or more bits.</p> + <p>A segment of type <c>binary</c> must have a size evenly + divisible by 8 (or divisible by the unit size, if the unit size has been changed).</p> + <p>A segment of type <c>bitstring</c> has no restrictions on the size.</p> + <p>As noted earlier, segments have the following general syntax:</p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>When matching <c>Value</c> value must be either a variable or + an integer or floating point literal. Expressions are not + allowed.</p> + <p><c>Size</c> must be an integer literal, or a previously bound + variable. Note that the following is not allowed:</p> + <code type="none"><![CDATA[ +foo(N, <<X:N,T/binary>>) -> + {X,T}.]]></code> + <p>The two occurrences of <c>N</c> are not related. The compiler + will complain that the <c>N</c> in the size field is unbound.</p> + <p>The correct way to write this example is like this:</p> + <code type="none"><![CDATA[ +foo(N, Bin) -> + <<X:N,T/binary>> = Bin, + {X,T}.]]></code> + + <section> + <title>Getting the Rest of the Binary or Bitstring</title> + <p>To match out the rest of a binary, specify a binary field + without size:</p> + <code type="none"><![CDATA[ +foo(<<A:8,Rest/binary>>) ->]]></code> + <p>The size of the tail must be evenly divisible by 8.</p> + + <p>To match out the rest of a bitstring, specify a field + without size:</p> + <code type="none"><![CDATA[ +foo(<<A:8,Rest/bitstring>>) ->]]></code> + <p>There is no restriction on the number of bits in the tail.</p> + </section> + </section> + + <section> + <title>Appending to a Binary</title> + <p>In R12B, the following function for creating a binary out of + a list of triples of integers is now efficient:</p> + <code type="none"><![CDATA[ +triples_to_bin(T) -> + triples_to_bin(T, <<>>). + +triples_to_bin([{X,Y,Z} | T], Acc) -> + triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>); % inefficient before R12B +triples_to_bin([], Acc) -> + Acc.]]></code> + <p>In previous releases, this function was highly inefficient, because + the binary constructed so far (<c>Acc</c>) was copied in each recursion step. + That is no longer the case. See the Efficiency Guide for more information.</p> + </section> +</chapter> + diff --git a/system/doc/programming_examples/book.xml b/system/doc/programming_examples/book.xml new file mode 100644 index 0000000000..91346ceea4 --- /dev/null +++ b/system/doc/programming_examples/book.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE book SYSTEM "book.dtd"> + +<book xmlns:xi="http://www.w3.org/2001/XInclude"> + <header titlestyle="normal"> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Programming Examples</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + </header> + <pagetext>Programming Examples</pagetext> + <preamble> + </preamble> + <parts lift="no"> + <xi:include href="part.xml"/> + </parts> +</book> + diff --git a/system/doc/programming_examples/fun_test.erl b/system/doc/programming_examples/fun_test.erl new file mode 100644 index 0000000000..8472fd87f8 --- /dev/null +++ b/system/doc/programming_examples/fun_test.erl @@ -0,0 +1,17 @@ +%1 +-module(fun_test). +-export([t1/0, t2/0, t3/0, t4/0, double/1]). +-import(lists, [map/2]). + +t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]). + +t2() -> map(fun double/1, [1,2,3,4,5]). + +t3() -> map({?MODULE, double}, [1,2,3,4,5]). + +double(X) -> X * 2. +%1 + + +t4() -> + "hello world". diff --git a/system/doc/programming_examples/funparse.erl b/system/doc/programming_examples/funparse.erl new file mode 100644 index 0000000000..5e23c90df9 --- /dev/null +++ b/system/doc/programming_examples/funparse.erl @@ -0,0 +1,74 @@ +-module(funparse). +-compile(export_all). +-import(lists, [reverse/1]). + +%17 +%% > hof:parse([a,c]). +%% {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} +%% > hof:parse([a,d]). +%% {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} +%% > hof:parse([b,c]). +%% {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} +%% > hof:parse([b,d]). +%% {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} +%% > hof:parse([a,b]). +%% fail +%17 + +%% Grammar = (a | b) & (c | d) + +%12 +parse(List) -> + (grammar())(List). +%12 + +%13 +grammar() -> + pand( + por(pconst(a), pconst(b)), + por(pconst(c), pconst(d))). +%13 + +%14 +pconst(X) -> + fun (T) -> + case T of + [X|T1] -> {ok, {const, X}, T1}; + _ -> fail + end + end. +%14 + +%15 +por(P1, P2) -> + fun (T) -> + case P1(T) of + {ok, R, T1} -> + {ok, {'or',1,R}, T1}; + fail -> + case P2(T) of + {ok, R1, T1} -> + {ok, {'or',2,R1}, T1}; + fail -> + fail + end + end + end. +%15 + +%16 +pand(P1, P2) -> + fun (T) -> + case P1(T) of + {ok, R1, T1} -> + case P2(T1) of + {ok, R2, T2} -> + {ok, {'and', R1, R2}}; + fail -> + fail + end; + fail -> + fail + end + end. +%16 diff --git a/system/doc/programming_examples/funs.xmlsrc b/system/doc/programming_examples/funs.xmlsrc new file mode 100644 index 0000000000..92f99cf6d3 --- /dev/null +++ b/system/doc/programming_examples/funs.xmlsrc @@ -0,0 +1,515 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Funs</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>funs.xml</file> + </header> + + <section> + <title>Example 1 - map</title> + <p>If we want to double every element in a list, we could write a + function named <c>double</c>:</p> + <code type="none"> +double([H|T]) -> [2*H|double(T)]; +double([]) -> [].</code> + <p>This function obviously doubles the argument entered as input + as follows:</p> + <pre> +> <input>double([1,2,3,4]).</input> +[2,4,6,8]</pre> + <p>We now add the function <c>add_one</c>, which adds one to every + element in a list:</p> + <code type="none"> +add_one([H|T]) -> [H+1|add_one(T)]; +add_one([]) -> [].</code> + <p>These functions, <c>double</c> and <c>add_one</c>, have a very + similar structure. We can exploit this fact and write a function + <c>map</c> which expresses this similarity:</p> + <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> + <p>We can now express the functions <c>double</c> and + <c>add_one</c> in terms of <c>map</c> as follows:</p> + <code type="none"> +double(L) -> map(fun(X) -> 2*X end, L). +add_one(L) -> map(fun(X) -> 1 + X end, L).</code> + <p><c>map(F, List)</c> is a function which takes a function + <c>F</c> and a list <c>L</c> as arguments and returns the new + list which is obtained by applying <c>F</c> to each of + the elements in <c>L</c>.</p> + <p>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:</p> + <list type="ordered"> + <item>write one function which represents the common features of + these functions</item> + <item>parameterize the difference in terms of functions which + are passed as arguments to the common function.</item> + </list> + </section> + + <section> + <title>Example 2 - foreach</title> + <p>This example illustrates procedural abstraction. Initially, we + show the following two examples written as conventional + functions:</p> + <list type="ordered"> + <item>all elements of a list are printed onto a stream</item> + <item>a message is broadcast to a list of processes.</item> + </list> + <code type="none"> +print_list(Stream, [H|T]) -> + io:format(Stream, "~p~n", [H]), + print_list(Stream, T); +print_list(Stream, []) -> + true.</code> + <code type="none"> +broadcast(Msg, [Pid|Pids]) -> + Pid ! Msg, + broadcast(Msg, Pids); +broadcast(_, []) -> + true.</code> + <p>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.</p> + <p>The function <c>foreach</c> expresses this similarity:</p> + <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> + <p>Using <c>foreach</c>, <c>print_list</c> becomes:</p> + <code type="none"> +foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L)</code> + <p><c>broadcast</c> becomes:</p> + <code type="none"> +foreach(fun(Pid) -> Pid ! M end, L)</code> + <p><c>foreach</c> is evaluated for its side-effect and not its + value. <c>foreach(Fun ,L)</c> calls <c>Fun(X)</c> for each + element <c>X</c> in <c>L</c> and the processing occurs in + the order in which the elements were defined in <c>L</c>. + <c>map</c> does not define the order in which its elements are + processed.</p> + </section> + + <section> + <title>The Syntax of Funs</title> + <p>Funs are written with the syntax:</p> + <code type="none"> +F = fun (Arg1, Arg2, ... ArgN) -> + ... + end</code> + <p>This creates an anonymous function of <c>N</c> arguments and + binds it to the variable <c>F</c>.</p> + <p>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:</p> + <code type="none"> +F = fun FunctionName/Arity</code> + <p>With this form of function reference, the function which is + referred to does not need to be exported from the module.</p> + <p>We can also refer to a function defined in a different module + with the following syntax:</p> + <code type="none"> +F = {Module, FunctionName}</code> + <p>In this case, the function must be exported from the module in + question.</p> + <p>The follow program illustrates the different ways of creating + funs:</p> + <codeinclude file="fun_test.erl" tag="%1" type="erl"></codeinclude> + <p>We can evaluate the fun <c>F</c> with the syntax:</p> + <code type="none"> +F(Arg1, Arg2, ..., Argn)</code> + <p>To check whether a term is a fun, use the test + <c>is_function/1</c> in a guard. Example:</p> + <code type="none"> +f(F, Args) when is_function(F) -> + apply(F, Args); +f(N, _) when is_integer(N) -> + N.</code> + <p>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 + contains funs that depend on the old version of a module.</p> + <note> + <p>In OTP R5 and earlier releases, funs were represented using + tuples.</p> + </note> + </section> + + <section> + <title>Variable Bindings Within a Fun</title> + <p>The scope rules for variables which occur in funs are as + follows:</p> + <list type="bulleted"> + <item>All variables which occur in the head of a fun are assumed + to be "fresh" variables.</item> + <item>Variables which are defined before the fun, and which + occur in function calls or guard tests within the fun, have + the values they had outside the fun.</item> + <item>No variables may be exported from a fun.</item> + </list> + <p>The following examples illustrate these rules:</p> + <code type="none"> +print_list(File, List) -> + {ok, Stream} = file:open(File, write), + foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List), + file:close(Stream).</code> + <p>In the above example, the variable <c>X</c> which is defined in + the head of the fun is a new variable. The value of the variable + <c>Stream</c> which is used within within the fun gets its value + from the <c>file:open</c> line.</p> + <p>Since any variable which occurs in the head of a fun is + considered a new variable it would be equally valid to write:</p> + <code type="none"> +print_list(File, List) -> + {ok, Stream} = file:open(File, write), + foreach(fun(File) -> + io:format(Stream,"~p~n",[File]) + end, List), + file:close(Stream).</code> + <p>In this example, <c>File</c> is used as the new variable + instead of <c>X</c>. This is rather silly since code in the body + of the fun cannot refer to the variable <c>File</c> which is + defined outside the fun. Compiling this example will yield + the diagnostic:</p> + <code type="none"> +./FileName.erl:Line: Warning: variable 'File' + shadowed in 'lambda head'</code> + <p>This reminds us that the variable <c>File</c> which is defined + inside the fun collides with the variable <c>File</c> which is + defined outside the fun.</p> + <p>The rules for importing variables into a fun has the consequence + that certain pattern matching operations have to 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 + the first clause of <c>F</c> to be evaluated when the value of + its argument is <c>Y</c>:</p> + <code type="none"> +f(...) -> + Y = ... + map(fun(X) when X == Y -> + ; + (_) -> + ... + end, ...) + ...</code> + <p>instead of</p> + <code type="none"> +f(...) -> + Y = ... + map(fun(Y) -> + ; + (_) -> + ... + end, ...) + ...</code> + </section> + + <section> + <title>Funs and the Module Lists</title> + <p>The following examples show a dialogue with the Erlang shell. + All the higher order functions discussed are exported from + the module <c>lists</c>.</p> + + <section> + <title>map</title> + <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> + <p><c>map</c> takes a function of one argument and a list of + terms. It returns the list obtained by applying the function + to every argument in the list.</p> + <pre> +> <input>Double = fun(X) -> 2 * X end.</input> +#Fun<erl_eval.6.72228031> +> <input>lists:map(Double, [1,2,3,4,5]).</input> +[2,4,6,8,10]</pre> + <p>When a new fun is defined in the shell, the value of the Fun + is printed as <c><![CDATA[Fun#<erl_eval>]]></c>.</p> + </section> + + <section> + <title>any</title> + <codeinclude file="funs1.erl" tag="%4" type="erl"></codeinclude> + <p><c>any</c> takes a predicate <c>P</c> of one argument and a + list of terms. A predicate is a function which returns + <c>true</c> or <c>false</c>. <c>any</c> is true if there is a + term <c>X</c> in the list such that <c>P(X)</c> is <c>true</c>.</p> + <p>We define a predicate <c>Big(X)</c> which is <c>true</c> if + its argument is greater that 10.</p> + <pre> +> <input>Big = fun(X) -> if X > 10 -> true; true -> false end end.</input> +#Fun<erl_eval.6.72228031> +> <input>lists:any(Big, [1,2,3,4]).</input> +false +> <input>lists:any(Big, [1,2,3,12,5]).</input> +true</pre> + </section> + + <section> + <title>all</title> + <codeinclude file="funs1.erl" tag="%3" type="erl"></codeinclude> + <p><c>all</c> has the same arguments as <c>any</c>. It is true + if the predicate applied to all elements in the list is true.</p> + <pre> +> <input>lists:all(Big, [1,2,3,4,12,6]).</input> +false +> <input>lists:all(Big, [12,13,14,15]).</input> +true</pre> + </section> + + <section> + <title>foreach</title> + <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> + <p><c>foreach</c> takes a function of one argument and a list of + terms. The function is applied to each argument in the list. + <c>foreach</c> returns <c>ok</c>. It is used for its + side-effect only.</p> + <pre> +> <input>lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]).</input> +1 +2 +3 +4 +ok</pre> + </section> + + <section> + <title>foldl</title> + <codeinclude file="funs1.erl" tag="%8" type="erl"></codeinclude> + <p><c>foldl</c> takes a function of two arguments, an + 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 function is called.</p> + <p>If we have a list of lists <c>L = ["I","like","Erlang"]</c>, + then we can sum the lengths of all the strings in <c>L</c> as + follows:</p> + <pre> +> <input>L = ["I","like","Erlang"].</input> +["I","like","Erlang"] +10> <input>lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L).</input> +11</pre> + <p><c>foldl</c> works like a <c>while</c> loop in an imperative + language:</p> + <code type="none"> +L = ["I","like","Erlang"], +Sum = 0, +while( L != []){ + Sum += length(head(L)), + L = tail(L) +end</code> + </section> + + <section> + <title>mapfoldl</title> + <codeinclude file="funs1.erl" tag="%10" type="erl"></codeinclude> + <p><c>mapfoldl</c> simultaneously maps and folds over a list. + The following example shows how to change all letters in + <c>L</c> to upper case and count them.</p> + <p>First upcase:</p> + <pre> +> <input>Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a;</input> +<input>(X) -> X</input> +<input>end.</input> +#Fun<erl_eval.6.72228031> +> <input>Upcase_word =</input> +<input>fun(X) -></input> +<input>lists:map(Upcase, X)</input> +<input>end.</input> +#Fun<erl_eval.6.72228031> +> <input>Upcase_word("Erlang").</input> +"ERLANG" +> <input>lists:map(Upcase_word, L).</input> +["I","LIKE","ERLANG"]</pre> + <p>Now we can do the fold and the map at the same time:</p> + <pre> +> <input>lists:mapfoldl(fun(Word, Sum) -></input> +<input>{Upcase_word(Word), Sum + length(Word)}</input> +<input>end, 0, L).</input> +{["I","LIKE","ERLANG"],11}</pre> + </section> + + <section> + <title>filter</title> + <codeinclude file="funs1.erl" tag="%9" type="erl"></codeinclude> + <p><c>filter</c> takes a predicate of one argument and a list + and returns all element in the list which satisfy + the predicate.</p> + <pre> +> <input>lists:filter(Big, [500,12,2,45,6,7]).</input> +[500,12,45]</pre> + <p>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 <c>diff(L1, L2)</c> to be + the difference between the lists <c>L1</c> and <c>L2</c>. + This is the list of all elements in L1 which are not contained + in L2. This code can be written as follows:</p> + <code type="none"> +diff(L1, L2) -> + filter(fun(X) -> not member(X, L2) end, L1).</code> + <p>The AND intersection of the list <c>L1</c> and <c>L2</c> is + also easily defined:</p> + <code type="none"> +intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).</code> + </section> + + <section> + <title>takewhile</title> + <codeinclude file="funs1.erl" tag="%5" type="erl"></codeinclude> + <p><c>takewhile(P, L)</c> takes elements <c>X</c> from a list + <c>L</c> as long as the predicate <c>P(X)</c> is true.</p> + <pre> +> <input>lists:takewhile(Big, [200,500,45,5,3,45,6]).</input> +[200,500,45]</pre> + </section> + + <section> + <title>dropwhile</title> + <codeinclude file="funs1.erl" tag="%6" type="erl"></codeinclude> + <p><c>dropwhile</c> is the complement of <c>takewhile</c>.</p> + <pre> +> <input>lists:dropwhile(Big, [200,500,45,5,3,45,6]).</input> +[5,3,45,6]</pre> + </section> + + <section> + <title>splitwith</title> + <codeinclude file="funs1.erl" tag="%7" type="erl"></codeinclude> + <p><c>splitwith(P, L)</c> splits the list <c>L</c> into the two + sub-lists <c>{L1, L2}</c>, where <c>L = takewhile(P, L)</c> + and <c>L2 = dropwhile(P, L)</c>.</p> + <pre> +> <input>lists:splitwith(Big, [200,500,45,5,3,45,6]).</input> +{[200,500,45],[5,3,45,6]}</pre> + </section> + </section> + + <section> + <title>Funs Which Return Funs</title> + <p>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.</p> + + <section> + <title>Simple Higher Order Functions</title> + <p><c>Adder(X)</c> is a function which, given <c>X</c>, returns + a new function <c>G</c> such that <c>G(K)</c> returns + <c>K + X</c>.</p> + <pre> +> <input>Adder = fun(X) -> fun(Y) -> X + Y end end.</input> +#Fun<erl_eval.6.72228031> +> <input>Add6 = Adder(6).</input> +#Fun<erl_eval.6.72228031> +> <input>Add6(10).</input> +16</pre> + </section> + + <section> + <title>Infinite Lists</title> + <p>The idea is to write something like:</p> + <code type="none"> +-module(lazy). +-export([ints_from/1]). +ints_from(N) -> + fun() -> + [N|ints_from(N+1)] + end.</code> + <p>Then we can proceed as follows:</p> + <pre> +> <input>XX = lazy:ints_from(1).</input> +#Fun<lazy.0.29874839> +> <input>XX().</input> +[1|#Fun<lazy.0.29874839>] +> <input>hd(XX()).</input> +1 +> <input>Y = tl(XX()).</input> +#Fun<lazy.0.29874839> +> <input>hd(Y()).</input> +2</pre> + <p>etc. - this is an example of "lazy embedding".</p> + </section> + + <section> + <title>Parsing</title> + <p>The following examples show parsers of the following type:</p> + <pre> +Parser(Toks) -> {ok, Tree, Toks1} | fail</pre> + <p><c>Toks</c> is the list of tokens to be parsed. A successful + parse returns <c>{ok, Tree, Toks1}</c>, where <c>Tree</c> is a + parse tree and <c>Toks1</c> is a tail of <c>Tree</c> which + contains symbols encountered after the structure which was + correctly parsed. Otherwise <c>fail</c> is returned.</p> + <p>The example which follows illustrates a simple, functional + parser which parses the grammar:</p> + <pre> +(a | b) & (c | d)</pre> + <p>The following code defines a function <c>pconst(X)</c> in + the module <c>funparse</c>, which returns a fun which parses a + list of tokens.</p> + <codeinclude file="funparse.erl" tag="%14" type="erl"></codeinclude> + <p>This function can be used as follows:</p> + <pre> +> <input>P1 = funparse:pconst(a).</input> +#Fun<funparse.0.22674075> +> <input>P1([a,b,c]).</input> +{ok,{const,a},[b,c]} +> <input>P1([x,y,z]).</input> +fail</pre> + <p>Next, we define the two higher order functions <c>pand</c> + and <c>por</c> which combine primitive parsers to produce more + complex parsers. Firstly <c>pand</c>:</p> + <codeinclude file="funparse.erl" tag="%16" type="erl"></codeinclude> + <p>Given a parser <c>P1</c> for grammar <c>G1</c>, and a parser + <c>P2</c> for grammar <c>G2</c>, <c>pand(P1, P2)</c> returns a + parser for the grammar which consists of sequences of tokens + which satisfy <c>G1</c> followed by sequences of tokens which + satisfy <c>G2</c>.</p> + <p><c>por(P1, P2)</c> returns a parser for the language + described by the grammar <c>G1</c> or <c>G2</c>.</p> + <codeinclude file="funparse.erl" tag="%15" type="erl"></codeinclude> + <p>The original problem was to parse the grammar + <c><![CDATA[(a | b) & (c | d)]]></c>. The following code addresses this + problem:</p> + <codeinclude file="funparse.erl" tag="%13" type="erl"></codeinclude> + <p>The following code adds a parser interface to the grammar:</p> + <codeinclude file="funparse.erl" tag="%12" type="erl"></codeinclude> + <p>We can test this parser as follows:</p> + <pre> +> <input>funparse:parse([a,c]).</input> +{ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} +> <input>funparse:parse([a,d]).</input> +{ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} +> <input>funparse:parse([b,c]).</input> +{ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} +> <input>funparse:parse([b,d]).</input> +{ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} +> <input>funparse:parse([a,b]).</input> +fail</pre> + </section> + </section> +</chapter> + diff --git a/system/doc/programming_examples/funs1.erl b/system/doc/programming_examples/funs1.erl new file mode 100644 index 0000000000..8cf20378ea --- /dev/null +++ b/system/doc/programming_examples/funs1.erl @@ -0,0 +1,125 @@ +-module(funs1). +-compile(export_all). +-import(lists, [reverse/1]). + +%1 +map(F, [H|T]) -> [F(H)|map(F, T)]; +map(F, []) -> []. +%1 + +%2 +foreach(F, [H|T]) -> + F(H), + foreach(F, T); +foreach(F, []) -> + ok. +%2 +% +%3 +all(Pred, [H|T]) -> + case Pred(H) of + true -> all(Pred, T); + false -> false + end; +all(Pred, []) -> + true. +%3 +%4 +any(Pred, [H|T]) -> + case Pred(H) of + true -> true; + false -> any(Pred, T) + end; +any(Pred, []) -> + false. +%4 +%5 +takewhile(Pred, [H|T]) -> + case Pred(H) of + true -> [H|takewhile(Pred, T)]; + false -> [] + end; +takewhile(Pred, []) -> + []. +%5 +%6 +dropwhile(Pred, [H|T]) -> + case Pred(H) of + true -> dropwhile(Pred, T); + false -> [H|T] + end; +dropwhile(Pred, []) -> + []. +%6 +%7 +splitwith(Pred, L) -> + splitwith(Pred, L, []). + +splitwith(Pred, [H|T], L) -> + case Pred(H) of + true -> splitwith(Pred, T, [H|L]); + false -> {reverse(L), [H|T]} + end; +splitwith(Pred, [], L) -> + {reverse(L), []}. +%7 + +flatmap(F, [Hd|Tail]) -> + F(Hd) ++ flatmap(F, Tail); +flatmap(F, []) -> []. + +%8 +foldl(F, Accu, [Hd|Tail]) -> + foldl(F, F(Hd, Accu), Tail); +foldl(F, Accu, []) -> Accu. +%8 +% +foldr(F, Accu, [Hd|Tail]) -> + F(Hd, foldr(F, Accu, Tail)); +foldr(F, Accu, []) -> Accu. +%9 +filter(F, [H|T]) -> + case F(H) of + true -> [H|filter(F, T)]; + false -> filter(F, T) + end; +filter(F, []) -> []. +%9 +%10 +mapfoldl(F, Accu0, [Hd|Tail]) -> + {R,Accu1} = F(Hd, Accu0), + {Rs,Accu2} = mapfoldl(F, Accu1, Tail), + {[R|Rs], Accu2}; +mapfoldl(F, Accu, []) -> {[], Accu}. +%10 +mapfoldr(F, Accu0, [Hd|Tail]) -> + {Rs,Accu1} = mapfoldr(F, Accu0, Tail), + {R,Accu2} = F(Hd, Accu1), + {[R|Rs],Accu2}; +mapfoldr(F, Accu, []) -> {[], Accu}. +%11 +first(Pred, [H|T]) -> + case Pred(H) of + true -> + {true, H}; + false -> + first(Pred, T) + end; +first(Pred, []) -> + false. +%11 +% +compose(F, G) -> + fun(X) -> + F(G(X)) + end. + +%20 +iterate(N, F) -> + iterate(N, N+1, F). + +iterate(Stop, Stop, _) -> + []; +iterate(N, Stop, Fun) -> + [Fun(N)|iterate(N+1, Stop, Fun)]. +%20 diff --git a/system/doc/programming_examples/list_comprehensions.xml b/system/doc/programming_examples/list_comprehensions.xml new file mode 100644 index 0000000000..825459238b --- /dev/null +++ b/system/doc/programming_examples/list_comprehensions.xml @@ -0,0 +1,206 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>List Comprehensions</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>list_comprehensions.xml</file> + </header> + + <section> + <title>Simple Examples</title> + <p>We start with a simple example:</p> + <pre> +> <input>[X || X <- [1,2,a,3,4,b,5,6], X > 3].</input> +[a,4,b,5,6]</pre> + <p>This should be read as follows:</p> + <quote> + <p>The list of X such that X is taken from the list + <c>[1,2,a,...]</c> and X is greater than 3.</p> + </quote> + <p>The notation <c><![CDATA[X <- [1,2,a,...]]]></c> is a generator and + the expression <c>X > 3</c> is a filter.</p> + <p>An additional filter can be added in order to restrict + the result to integers:</p> + <pre> +> <input>[X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].</input> +[4,5,6]</pre> + <p>Generators can be combined. For example, the Cartesian product + of two lists can be written as follows:</p> + <pre> +> <input>[{X, Y} || X <- [1,2,3], Y <- [a,b]].</input> +[{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}]</pre> + </section> + + <section> + <title>Quick Sort</title> + <p>The well known quick sort routine can be written as follows:</p> + <code type="none"><![CDATA[ +sort([Pivot|T]) -> + sort([ X || X <- T, X < Pivot]) ++ + [Pivot] ++ + sort([ X || X <- T, X >= Pivot]); +sort([]) -> [].]]></code> + <p>The expression <c><![CDATA[[X || X <- T, X < Pivot]]]></c> is the list of + all elements in <c>T</c>, which are less than <c>Pivot</c>.</p> + <p><c><![CDATA[[X || X <- T, X >= Pivot]]]></c> is the list of all elements in + <c>T</c>, which are greater or equal to <c>Pivot</c>.</p> + <p>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.</p> + </section> + + <section> + <title>Permutations</title> + <p>The following example generates all permutations of + the elements in a list:</p> + <code type="none"><![CDATA[ +perms([]) -> [[]]; +perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].]]></code> + <p>We take take <c>H</c> from <c>L</c> in all possible ways. + The result is the set of all lists <c>[H|T]</c>, where <c>T</c> + is the set of all possible permutations of <c>L</c> with + <c>H</c> removed.</p> + <pre> +> <input>perms([b,u,g]).</input> +[[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]]</pre> + </section> + + <section> + <title>Pythagorean Triplets</title> + <p>Pythagorean triplets are sets of integers <c>{A,B,C}</c> such + that <c>A**2 + B**2 = C**2</c>.</p> + <p>The function <c>pyth(N)</c> generates a list of all integers + <c>{A,B,C}</c> such that <c>A**2 + B**2 = C**2</c> and where + the sum of the sides is equal to or less than <c>N</c>.</p> + <code type="none"><![CDATA[ +pyth(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N), + C <- lists:seq(1,N), + A+B+C =< N, + A*A+B*B == C*C + ].]]></code> + <pre> +> <input>pyth(3).</input> +[]. +> <input>pyth(11).</input> +[]. +> <input>pyth(12).</input> +[{3,4,5},{4,3,5}] +> <input>pyth(50).</input> +[{3,4,5}, + {4,3,5}, + {5,12,13}, + {6,8,10}, + {8,6,10}, + {8,15,17}, + {9,12,15}, + {12,5,13}, + {12,9,15}, + {12,16,20}, + {15,8,17}, + {16,12,20}]</pre> + <p>The following code reduces the search space and is more + efficient:</p> + <code type="none"><![CDATA[ +pyth1(N) -> + [{A,B,C} || + A <- lists:seq(1,N-2), + B <- lists:seq(A+1,N-1), + C <- lists:seq(B+1,N), + A+B+C =< N, + A*A+B*B == C*C ].]]></code> + </section> + + <section> + <title>Simplifications with List Comprehensions</title> + <p>As an example, list comprehensions can be used to simplify some + of the functions in <c>lists.erl</c>:</p> + <code type="none"><![CDATA[ +append(L) -> [X || L1 <- L, X <- L1]. +map(Fun, L) -> [Fun(X) || X <- L]. +filter(Pred, L) -> [X || X <- L, Pred(X)].]]></code> + </section> + + <section> + <title>Variable Bindings in List Comprehensions</title> + <p>The scope rules for variables which occur in list + comprehensions are as follows:</p> + <list type="bulleted"> + <item>all variables which occur in a generator pattern are + assumed to be "fresh" variables</item> + <item>any variables which are defined before the list + comprehension and which are used in filters have the values + they had before the list comprehension</item> + <item>no variables may be exported from a list comprehension.</item> + </list> + <p>As an example of these rules, suppose we want to write + the function <c>select</c>, which selects certain elements from + a list of tuples. We might write + <c><![CDATA[select(X, L) -> [Y || {X, Y} <- L].]]></c> with the intention + of extracting all tuples from <c>L</c> where the first item is + <c>X</c>.</p> + <p>Compiling this yields the following diagnostic:</p> + <code type="none"> +./FileName.erl:Line: Warning: variable 'X' shadowed in generate</code> + <p>This diagnostic warns us that the variable <c>X</c> in + the pattern is not the same variable as the variable <c>X</c> + which occurs in the function head.</p> + <p>Evaluating <c>select</c> yields the following result:</p> + <pre> +> <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> +[1,2,3,7]</pre> + <p>This result is not what we wanted. To achieve the desired + effect we must write <c>select</c> as follows:</p> + <code type="none"><![CDATA[ +select(X, L) -> [Y || {X1, Y} <- L, X == X1].]]></code> + <p>The generator now contains unbound variables and the test has + been moved into the filter. This now works as expected:</p> + <pre> +> <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> +[2,7]</pre> + <p>One 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:</p> + <code type="none"><![CDATA[ +f(...) -> + Y = ... + [ Expression || PatternInvolving Y <- Expr, ...] + ...]]></code> + <p>Instead, write as follows:</p> + <code type="none"><![CDATA[ +f(...) -> + Y = ... + [ Expression || PatternInvolving Y1 <- Expr, Y == Y1, ...] + ...]]></code> + </section> +</chapter> + diff --git a/system/doc/programming_examples/make.dep b/system/doc/programming_examples/make.dep new file mode 100644 index 0000000000..b0655f56b3 --- /dev/null +++ b/system/doc/programming_examples/make.dep @@ -0,0 +1,20 @@ +# ---------------------------------------------------- +# >>>> Do not edit this file <<<< +# This file was automaticly generated by +# /home/otp/bin/docdepend +# ---------------------------------------------------- + + +# ---------------------------------------------------- +# TeX files that the DVI file depend on +# ---------------------------------------------------- + +book.dvi: bit_syntax.tex book.tex funs.tex list_comprehensions.tex \ + part.tex records.tex + +# ---------------------------------------------------- +# Source inlined when transforming from source to LaTeX +# ---------------------------------------------------- + +funs.tex: fun_test.erl funparse.erl funs1.erl + diff --git a/system/doc/programming_examples/part.xml b/system/doc/programming_examples/part.xml new file mode 100644 index 0000000000..5b22ddf82f --- /dev/null +++ b/system/doc/programming_examples/part.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Programming Examples</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + </header> + <description> + <p>This chapter contains examples on using records, funs, list + comprehensions and the bit syntax.</p> + </description> + <xi:include href="records.xml"/> + <xi:include href="funs.xml"/> + <xi:include href="list_comprehensions.xml"/> + <xi:include href="bit_syntax.xml"/> +</part> + diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml new file mode 100644 index 0000000000..2f2434f11c --- /dev/null +++ b/system/doc/programming_examples/records.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Records</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>records.xml</file> + </header> + + <section> + <title>Records vs Tuples</title> + <p>The main advantage of using records instead of 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 + <c>{Name, Address, Phone}</c>.</p> + <p>We must remember that the <c>Name</c> field is the first + element of the tuple, the <c>Address</c> 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 <c>P</c> + which contains such a tuple we might write the following code + and then use pattern matching to extract the relevant fields.</p> + <code type="none"> +Name = element(1, P), +Address = element(2, P), +...</code> + <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.</p> + <p>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.</p> + <code type="none"> +-record(person, {name, phone, address}).</code> + <p>For example, if <c>P</c> is now a variable whose value is a + <c>person</c> record, we can code as follows in order to access + the name and address fields of the records.</p> + <code type="none"> +Name = P#person.name, +Address = P#person.address, +...</code> + <p>Internally, records are represented using tagged tuples:</p> + <code type="none"> +{person, Name, Phone, Address}</code> + </section> + + <section> + <title>Defining a Record</title> + <p>This definition of a person will be used in many of + the examples which follow. It contains three fields, <c>name</c>, + <c>phone</c> and <c>address</c>. The default values for + <c>name</c> and <c>phone</c> is "" and [], respectively. + The default value for <c>address</c> is the atom + <c>undefined</c>, since no default value is supplied for this + field:</p> + <pre> +-record(person, {name = "", phone = [], address}).</pre> + <p>We have to define the record in the shell in order to be able + use the record syntax in the examples:</p> + <pre> +> <input>rd(person, {name = "", phone = [], address}).</input> +person</pre> + <p>This is due to the fact that record definitions are available + at compile time only, not at runtime. See <c>shell(3)</c> for + details on records in the shell. + </p> + </section> + + <section> + <title>Creating a Record</title> + <p>A new <c>person</c> record is created as follows:</p> + <pre> +> <input>#person{phone=[0,8,2,3,4,3,1,2], name="Robert"}.</input> +#person{name = "Robert",phone = [0,8,2,3,4,3,1,2],address = undefined}</pre> + <p>Since the <c>address</c> field was omitted, its default value + is used.</p> + <p>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 <c>_</c>, means "all fields not explicitly specified".</p> + <pre> +> <input>#person{name = "Jakob", _ = '_'}.</input> +#person{name = "Jakob",phone = '_',address = '_'}</pre> + <p>It is primarily intended to be used in <c>ets:match/2</c> and + <c>mnesia:match_object/3</c>, to set record fields to the atom + <c>'_'</c>. (This is a wildcard in <c>ets:match/2</c>.)</p> + </section> + + <section> + <title>Accessing a Record Field</title> + <pre> +> <input>P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.</input> +#person{name = "Joe",phone = [0,8,2,3,4,3,1,2],address = undefined} +> <input>P#person.name.</input> +"Joe"</pre> + </section> + + <section> + <title>Updating a Record</title> + <pre> +> <input>P1 = #person{name="Joe", phone=[1,2,3], address="A street"}.</input> +#person{name = "Joe",phone = [1,2,3],address = "A street"} +> <input>P2 = P1#person{name="Robert"}.</input> +#person{name = "Robert",phone = [1,2,3],address = "A street"}</pre> + </section> + + <section> + <title>Type Testing</title> + <p>The following example shows that the guard succeeds if + <c>P</c> is record of type <c>person</c>.</p> + <pre> +foo(P) when is_record(P, person) -> a_person; +foo(_) -> not_a_person.</pre> + </section> + + <section> + <title>Pattern Matching</title> + <p>Matching can be used in combination with records as shown in + the following example:</p> + <pre> +> <input>P3 = #person{name="Joe", phone=[0,0,7], address="A street"}.</input> +#person{name = "Joe",phone = [0,0,7],address = "A street"} +> <input>#person{name = Name} = P3, Name.</input> +"Joe"</pre> + <p>The following function takes a list of <c>person</c> records + and searches for the phone number of a person with a particular + name:</p> + <code type="none"> +find_phone([#person{name=Name, phone=Phone} | _], Name) -> + {found, Phone}; +find_phone([_| T], Name) -> + find_phone(T, Name); +find_phone([], Name) -> + not_found.</code> + <p>The fields referred to in the pattern can be given in any order.</p> + </section> + + <section> + <title>Nested Records</title> + <p>The value of a field in a record might 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:</p> + <pre> +-record(name, {first = "Robert", last = "Ericsson"}). +-record(person, {name = #name{}, phone}). + +demo() -> + P = #person{name= #name{first="Robert",last="Virding"}, phone=123}, + First = (P#person.name)#name.first.</pre> + <p>In this example, <c>demo()</c> evaluates to <c>"Robert"</c>.</p> + </section> + + <section> + <title>Example</title> + <pre> +%% File: person.hrl + +%%----------------------------------------------------------- +%% Data Type: person +%% where: +%% name: A string (default is undefined). +%% age: An integer (default is undefined). +%% phone: A list of integers (default is []). +%% dict: A dictionary containing various information +%% about the person. +%% A {Key, Value} list (default is the empty list). +%%------------------------------------------------------------ +-record(person, {name, age, phone = [], dict = []}).</pre> + <pre> +-module(person). +-include("person.hrl"). +-compile(export_all). % For test purposes only. + +%% This creates an instance of a person. +%% Note: The phone number is not supplied so the +%% default value [] will be used. + +make_hacker_without_phone(Name, Age) -> + #person{name = Name, age = Age, + dict = [{computer_knowledge, excellent}, + {drinks, coke}]}. + +%% This demonstrates matching in arguments + +print(#person{name = Name, age = Age, + phone = Phone, dict = Dict}) -> + io:format("Name: ~s, Age: ~w, Phone: ~w ~n" + "Dictionary: ~w.~n", [Name, Age, Phone, Dict]). + +%% Demonstrates type testing, selector, updating. + +birthday(P) when record(P, person) -> + P#person{age = P#person.age + 1}. + +register_two_hackers() -> + Hacker1 = make_hacker_without_phone("Joe", 29), + OldHacker = birthday(Hacker1), + % The central_register_server should have + % an interface function for this. + central_register_server ! {register_person, Hacker1}, + central_register_server ! {register_person, + OldHacker#person{name = "Robert", + phone = [0,8,3,2,4,5,3,1]}}.</pre> + </section> +</chapter> + diff --git a/system/doc/programming_examples/xmlfiles.mk b/system/doc/programming_examples/xmlfiles.mk new file mode 100644 index 0000000000..5eb42a2881 --- /dev/null +++ b/system/doc/programming_examples/xmlfiles.mk @@ -0,0 +1,23 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +PROG_EX_CHAPTER_FILES = \ + bit_syntax.xml \ + funs.xml \ + list_comprehensions.xml \ + records.xml |