├── source ├── de │ ├── package.ceylon │ ├── module.ceylon │ ├── 07benannte_argumente.ceylon │ ├── 06schnittstellen_operatoren.ceylon │ ├── 02klassen.ceylon │ ├── 05aufzählungen_folgen.ceylon │ ├── 01grundlagen.ceylon │ ├── 03null_vereinigung_schnitt.ceylon │ └── 04funktionen.ceylon ├── en │ ├── module.ceylon │ ├── package.ceylon │ ├── 06interfaces_operators.ceylon │ ├── 07namedarguments.ceylon │ ├── 02classes.ceylon │ ├── 05iterables_sequences.ceylon │ ├── 03null_unions_intersections.ceylon │ ├── 01basics.ceylon │ └── 04functions.ceylon ├── es │ ├── module.ceylon │ ├── package.ceylon │ ├── 06interfaces_operadores.ceylon │ ├── 07argumentos_con_nombre.ceylon │ ├── 01bases.ceylon │ ├── 02clases.ceylon │ ├── 05iterables_secuencias.ceylon │ ├── 03null_union_interseccion.ceylon │ └── 04funciones.ceylon └── fr │ ├── module.ceylon │ ├── package.ceylon │ ├── 06interfaces_operateurs.ceylon │ ├── 07arguments_nommes.ceylon │ ├── 02classes.ceylon │ ├── 05iterables_sequences.ceylon │ ├── 01bases.ceylon │ └── 03null_unions_intersections.ceylon ├── .ceylon ├── ide-config ├── bootstrap │ ├── ceylon-bootstrap.jar │ └── ceylon-bootstrap.properties └── config ├── .idea ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── modules.xml ├── compiler.xml └── misc.xml ├── .gitignore ├── .settings └── org.eclipse.core.resources.prefs ├── README.md ├── .classpath ├── .project ├── ceylon-walkthrough.iml ├── ceylonb └── ceylonb.bat /source/de/package.ceylon: -------------------------------------------------------------------------------- 1 | "Die Ceylon-Demo auf Deutsch." 2 | shared package de; 3 | -------------------------------------------------------------------------------- /source/en/module.ceylon: -------------------------------------------------------------------------------- 1 | module en "1" { 2 | import ceylon.collection "1.3.3"; 3 | } -------------------------------------------------------------------------------- /source/en/package.ceylon: -------------------------------------------------------------------------------- 1 | "The Ceylon walkthrough in English." 2 | shared package en; 3 | -------------------------------------------------------------------------------- /source/es/module.ceylon: -------------------------------------------------------------------------------- 1 | module es "1" { 2 | import ceylon.collection "1.3.3"; 3 | } -------------------------------------------------------------------------------- /source/es/package.ceylon: -------------------------------------------------------------------------------- 1 | "El recorrido de Ceylon en español." 2 | shared package es; 3 | -------------------------------------------------------------------------------- /source/de/module.ceylon: -------------------------------------------------------------------------------- 1 | module de "1.0.0" { 2 | import ceylon.collection "1.3.3"; 3 | } -------------------------------------------------------------------------------- /source/fr/module.ceylon: -------------------------------------------------------------------------------- 1 | module fr "1.0.0" { 2 | import ceylon.collection "1.3.3"; 3 | } -------------------------------------------------------------------------------- /source/fr/package.ceylon: -------------------------------------------------------------------------------- 1 | "Visite guidée de Ceylon en français" 2 | shared package fr; 3 | -------------------------------------------------------------------------------- /.ceylon/ide-config: -------------------------------------------------------------------------------- 1 | 2 | [project] 3 | compile-jvm=true 4 | compile-js=false 5 | system-repository= 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.ceylon/bootstrap/ceylon-bootstrap.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceylon/ceylon-walkthrough/HEAD/.ceylon/bootstrap/ceylon-bootstrap.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /classes 2 | /modules 3 | 4 | /.exploded/ 5 | 6 | /.idea/libraries 7 | /.idea/workspace.xml 8 | 9 | *~ 10 | \#* 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//source/es/package.ceylon=UTF-8 3 | encoding/=UTF-8 4 | -------------------------------------------------------------------------------- /.ceylon/bootstrap/ceylon-bootstrap.properties: -------------------------------------------------------------------------------- 1 | #Generated by 'ceylon bootstrap' 2 | #Thu Mar 16 09:27:33 PDT 2017 3 | distribution=https\://ceylon-lang.org/download/dist/1_3_2 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.ceylon/config: -------------------------------------------------------------------------------- 1 | 2 | [defaults] 3 | encoding=UTF-8 4 | offline=false 5 | flatclasspath=false 6 | autoexportmavendependencies=false 7 | fullyexportmavendependencies=false 8 | 9 | [compiler] 10 | suppresswarning=filenameNonAscii 11 | suppresswarning=deprecation 12 | suppresswarning=unusedDeclaration 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ceylon Walkthrough 2 | ================== 3 | 4 | An IDE-based walkthrough of the whole language. Check out this 5 | project in Eclipse or IntelliJ to get started. You must install 6 | Ceylon IDE first. 7 | 8 | Instructions on getting started are available at 9 | . 10 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ceylon-walkthrough 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | com.redhat.ceylon.eclipse.ui.ceylonBuilder 15 | 16 | 17 | explodeModules 18 | true 19 | 20 | 21 | systemRepo 22 | 23 | 24 | 25 | 26 | 27 | 28 | com.redhat.ceylon.eclipse.ui.ceylonNature 29 | org.eclipse.jdt.core.javanature 30 | 31 | 32 | -------------------------------------------------------------------------------- /ceylon-walkthrough.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ceylonb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # resolve links - $0 may be a softlink 4 | PRG="$0" 5 | while [ -h "$PRG" ]; do 6 | ls="$(ls -ld "$PRG")" 7 | link="${ls##*-> }" # remove largest prefix: yields link target (behind ->) 8 | if [ "$link" != "${link#/}" ]; then # remove prefix / if present 9 | # path was absolute 10 | PRG="$link" 11 | else 12 | # was not 13 | PRG="$(dirname "$PRG")/$link" 14 | fi 15 | done 16 | 17 | DIR="$(dirname "$PRG")" 18 | 19 | # Check if we should use a distribution bootstrap 20 | if [ -f "$DIR/.ceylon/bootstrap/ceylon-bootstrap.properties" ] && [ -f "$DIR/.ceylon/bootstrap/ceylon-bootstrap.jar" ]; then 21 | # Using bootstrap 22 | LIB="$DIR/.ceylon/bootstrap" 23 | else 24 | # Normal execution 25 | CEYLON_HOME="$DIR/.." 26 | LIB="$CEYLON_HOME/lib" 27 | 28 | if [ "$1" = "--show-home" ]; then 29 | echo "$CEYLON_HOME" 30 | exit 31 | fi 32 | fi 33 | 34 | if [ -z "$JAVA_HOME" ]; then 35 | JAVA="java" 36 | else 37 | JAVA="$JAVA_HOME/bin/java" 38 | fi 39 | 40 | # Make sure we have java installed 41 | if ! hash java 2>&- 42 | then 43 | echo >&2 "Java not found, you must install Java in order to compile and run Ceylon programs" 44 | echo >&2 "Go to http://www.java.com/getjava/ to download the latest version of Java" 45 | exit 1 46 | fi 47 | 48 | #JAVA_DEBUG_OPTS="-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y" 49 | 50 | if [ "$PRESERVE_JAVA_OPTS" != "true" ]; then 51 | PREPEND_JAVA_OPTS="$JAVA_DEBUG_OPTS" 52 | if [ -n "$COLUMNS" ]; then 53 | CEYL_COLS="$COLUMNS" 54 | elif stty size 2>/dev/null >/dev/null; then 55 | CEYL_COLS="$(stty size 2>/dev/null | cut -d' ' -f2)" 56 | else 57 | CEYL_COLS="$(tput 2>/dev/null cols)" 58 | fi 59 | PREPEND_JAVA_OPTS="$PREPEND_JAVA_OPTS -Dcom.redhat.ceylon.common.tool.terminal.width=$CEYL_COLS" 60 | PREPEND_JAVA_OPTS="$PREPEND_JAVA_OPTS -Dcom.redhat.ceylon.common.tool.progname=$(basename "$PRG")" 61 | fi 62 | for arg; do 63 | case $arg in 64 | --java=*) JAVA_OPTS="$JAVA_OPTS ${arg#--java=}";; 65 | [!-]*) break;; 66 | esac 67 | done 68 | JAVA_OPTS="$PREPEND_JAVA_OPTS $JAVA_OPTS" 69 | 70 | BOOTSTRAP="$LIB/ceylon-bootstrap.jar" 71 | 72 | # Check for cygwin, convert bootstrap path to Windows format 73 | case "`uname`" in 74 | CYGWIN*) [ -n "$LIB" ] && BOOTSTRAP=`cygpath -w "$BOOTSTRAP"` 75 | esac 76 | 77 | exec "$JAVA" \ 78 | $JAVA_OPTS \ 79 | -jar "$BOOTSTRAP" \ 80 | "$@" 81 | 82 | -------------------------------------------------------------------------------- /ceylonb.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal ENABLEDELAYEDEXPANSION 3 | 4 | :: Check if we should use a distribution bootstrap 5 | pushd "%~dp0" 6 | set "DIR=%CD%" 7 | popd 8 | if NOT exist "%DIR%\.ceylon\bootstrap\ceylon-bootstrap.properties" ( 9 | goto :normal 10 | ) 11 | if NOT exist "%DIR%\.ceylon\bootstrap\ceylon-bootstrap.jar" ( 12 | goto :normal 13 | ) 14 | 15 | :: Using bootstrap 16 | set "LIB=%DIR%\.ceylon\bootstrap" 17 | 18 | goto :endbs 19 | 20 | :normal 21 | 22 | :: Normal execution 23 | 24 | :: Find CEYLON_HOME 25 | pushd "%~dp0.." 26 | set "CEYLON_HOME=%CD%" 27 | popd 28 | set "LIB=%CEYLON_HOME%\lib" 29 | 30 | if "%~1" == "--show-home" ( 31 | @echo %CEYLON_HOME% 32 | exit /b 1 33 | ) 34 | 35 | :endbs 36 | 37 | :: Find Java 38 | 39 | :: Only check the registry if JAVA_HOME is not already set 40 | IF NOT "%JAVA_HOME%" == "" ( 41 | goto :javaend 42 | ) 43 | 44 | :: Find Java in the registry 45 | set "KEY_NAME=HKLM\SOFTWARE\JavaSoft\Java Runtime Environment" 46 | set "KEY_NAME2=HKLM\SOFTWARE\Wow6432Node\JavaSoft\Java Runtime Environment" 47 | 48 | :: get the current version 49 | FOR /F "usebackq skip=2 tokens=3" %%A IN (`REG QUERY "%KEY_NAME%" /v CurrentVersion 2^>nul`) DO ( 50 | set "ValueValue=%%A" 51 | ) 52 | 53 | if "%ValueValue%" NEQ "" ( 54 | set "JAVA_CURRENT=%KEY_NAME%\%ValueValue%" 55 | ) else ( 56 | rem Try again for 64bit systems 57 | 58 | FOR /F "usebackq skip=2 tokens=3" %%A IN (`REG QUERY "%KEY_NAME2%" /v CurrentVersion 2^>nul`) DO ( 59 | set "JAVA_CURRENT=%KEY_NAME2%\%%A" 60 | ) 61 | ) 62 | 63 | if "%ValueValue%" NEQ "" ( 64 | set "JAVA_CURRENT=%KEY_NAME%\%ValueValue%" 65 | ) else ( 66 | rem Try again for 64bit systems from a 32-bit process 67 | 68 | FOR /F "usebackq skip=2 tokens=3" %%A IN (`REG QUERY "%KEY_NAME%" /v CurrentVersion /reg:64 2^>nul`) DO ( 69 | set "JAVA_CURRENT=%KEY_NAME%\%%A" 70 | ) 71 | ) 72 | 73 | if "%JAVA_CURRENT%" == "" ( 74 | @echo Java not found, you must install Java in order to compile and run Ceylon programs 75 | @echo Go to http://www.java.com/getjava/ to download the latest version of Java 76 | exit /b 1 77 | ) 78 | 79 | :: get the javahome 80 | FOR /F "usebackq skip=2 tokens=3*" %%A IN (`REG QUERY "%JAVA_CURRENT%" /v JavaHome 2^>nul`) DO ( 81 | set "JAVA_HOME=%%A %%B" 82 | ) 83 | 84 | if "%JAVA_HOME%" EQU "" ( 85 | rem Try again for 64bit systems from a 32-bit process 86 | FOR /F "usebackq skip=2 tokens=3*" %%A IN (`REG QUERY "%JAVA_CURRENT%" /v JavaHome /reg:64 2^>nul`) DO ( 87 | set "JAVA_HOME=%%A %%B" 88 | ) 89 | ) 90 | 91 | :javaend 92 | 93 | set "JAVA=%JAVA_HOME%\bin\java.exe" 94 | 95 | :: Check that Java executable actually exists 96 | if not exist "%JAVA%" ( 97 | @echo "Cannot find java.exe at %JAVA%, check that your JAVA_HOME variable is pointing to the right place" 98 | exit /b 1 99 | ) 100 | 101 | rem set JAVA_DEBUG_OPTS="-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y" 102 | 103 | if NOT "%PRESERVE_JAVA_OPTS%" == "true" ( 104 | set PREPEND_JAVA_OPTS=%JAVA_DEBUG_OPTS% 105 | rem Other java opts go here 106 | ) 107 | 108 | rem Find any --java options and add their values to JAVA_OPTS 109 | for %%x in (%*) do ( 110 | set ARG=%%~x 111 | if "!ARG:~0,7!" EQU "--java=" ( 112 | set OPT=!ARG:~7! 113 | set "JAVA_OPTS=!JAVA_OPTS! !OPT!" 114 | ) else if "!ARG!" EQU "--java" ( 115 | @echo Error: use --java options with an equal sign and quotes, eg: "--java=-Xmx500m" 116 | exit /b 1 117 | ) else if "!ARG:~0,1!" NEQ "-" ( 118 | goto :breakloop 119 | ) 120 | ) 121 | :breakloop 122 | 123 | set "JAVA_OPTS=%PREPEND_JAVA_OPTS% %JAVA_OPTS%" 124 | 125 | "%JAVA%" ^ 126 | %JAVA_OPTS% ^ 127 | -jar "%LIB%\ceylon-bootstrap.jar" ^ 128 | %* 129 | 130 | endlocal 131 | 132 | if %errorlevel% neq 0 exit /B %errorlevel% 133 | -------------------------------------------------------------------------------- /source/en/06interfaces_operators.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | An interface defines a contract that can be 4 | satisfied by a class. Interfaces may have formal 5 | members, or even concrete members: 6 | 7 | - methods, 8 | - getters and setters, and 9 | - member classes. 10 | 11 | But an interface may not: 12 | 13 | - have an attribute that holds a reference to a 14 | value, or 15 | - define initialization logic. 16 | 17 | Thus, we say that interfaces are stateless. This 18 | means that "diamond" inheritance is unproblematic 19 | in Ceylon. 20 | 21 | */ 22 | 23 | interface Writer { 24 | 25 | shared formal void write(String string); 26 | 27 | shared void writeLine(String string) { 28 | write(string + operatingSystem.newline); 29 | } 30 | 31 | } 32 | 33 | /* 34 | 35 | A class may satisfy the interface, inheriting its 36 | members. 37 | 38 | */ 39 | 40 | class FunctionWriter(void fun(String string)) 41 | satisfies Writer { 42 | write = fun; 43 | } 44 | 45 | shared void testFunctionWriter() { 46 | FunctionWriter(process.write).writeLine("Hello!"); 47 | FunctionWriter(process.writeError).writeLine("Ooops!"); 48 | } 49 | 50 | /* 51 | 52 | A class may satisfy multiple interfaces. We 53 | use the & symbol to separate the supertypes 54 | because they conceptually form an intersection 55 | of types. 56 | 57 | */ 58 | 59 | class TextWriter(StringBuilder stringBuilder) 60 | satisfies Writer & Category<> { 61 | 62 | contains(Object element) 63 | => element in stringBuilder.string; 64 | 65 | write(String string) 66 | => stringBuilder.append(string); 67 | 68 | } 69 | 70 | shared void testTextWriter() { 71 | 72 | value textWriter = TextWriter(StringBuilder()); 73 | textWriter.writeLine("Hello :-)"); 74 | textWriter.writeLine("Goodbye :-("); 75 | 76 | // "x in category" means "category.contains(x)" 77 | // where category is an instance of Category 78 | assert(":-)" in textWriter); 79 | 80 | } 81 | 82 | /* 83 | 84 | It's especially common to have anonymous classes 85 | that satisfy an interface. 86 | 87 | */ 88 | 89 | object consoleWriter satisfies Writer { 90 | write = process.write; 91 | } 92 | 93 | shared void testConsoleWriter() { 94 | consoleWriter.writeLine("Hello!"); 95 | consoleWriter.writeLine("Bye!"); 96 | } 97 | 98 | /* 99 | 100 | The above anonymous class is a singleton, because 101 | it occurs as a toplevel declaration. But not 102 | every anonymous class is a singleton. 103 | 104 | */ 105 | 106 | //this anonymous class is a singleton 107 | object naturals 108 | satisfies Iterable { 109 | 110 | shared actual Iterator iterator() { 111 | //a new instance of this anonymous class 112 | //is created each time iterator() is 113 | //called 114 | object iterator 115 | satisfies Iterator { 116 | variable value int = 1; 117 | next() => int++; 118 | } 119 | return iterator; 120 | } 121 | 122 | } 123 | 124 | /* 125 | 126 | The anonymous class above satisfies Iterable. 127 | That means we can iterate it with a for loop. 128 | 129 | */ 130 | 131 | shared void loop() { 132 | for (n in naturals) { 133 | print(n); 134 | } 135 | } 136 | 137 | /* 138 | 139 | Many of the "built-in" language constructs are 140 | defined in terms of interfaces that your own 141 | classes may implement. In particular, most of the 142 | operators are defined in terms of interfaces. 143 | 144 | Let's see how we can define a complex number type 145 | that works just like the built-in numeric types. 146 | 147 | */ 148 | 149 | "A complex number class demonstrating operator 150 | polymorphism in Ceylon." 151 | class Complex(shared Float re, shared Float im=0.0) 152 | satisfies Exponentiable { 153 | 154 | negated => Complex(-re,-im); 155 | 156 | plus(Complex other) => Complex(re+other.re, im+other.im); 157 | 158 | minus(Complex other) => Complex(re-other.re, im-other.im); 159 | 160 | times(Complex other) => 161 | Complex(re*other.re-im*other.im, 162 | re*other.im+im*other.re); 163 | 164 | shared actual Complex divided(Complex other) { 165 | Float d = other.re^2 + other.im^2; 166 | return Complex((re*other.re+im*other.im)/d, 167 | (im*other.re-re*other.im)/d); 168 | } 169 | 170 | "Accepts non-negative powers." 171 | shared actual Complex power(Integer other) { 172 | "exponent must be non-negative" 173 | assert(other>=0); 174 | //lame impl 175 | variable Complex result = Complex(1.0, 0.0); 176 | for (i in 0:other) { 177 | result*=this; 178 | } 179 | return result; 180 | } 181 | 182 | string => im<0.0 then "``re``-``-im``i" 183 | else "``re``+``im``i"; 184 | 185 | hash => re.hash + im.hash; 186 | 187 | shared actual Boolean equals(Object that) { 188 | if (is Complex that) { 189 | return re==that.re && im==that.im; 190 | } 191 | else { 192 | return false; 193 | } 194 | } 195 | 196 | } 197 | 198 | Complex i = Complex(0.0, 1.0); 199 | 200 | shared void testComplex() { 201 | 202 | Complex one = Complex(1.0); 203 | Complex zero = Complex(0.0); 204 | 205 | Complex sum = one+i+zero; 206 | assert (sum==Complex(1.0, 1.0)); 207 | 208 | Complex zeroProduct = one*zero; 209 | assert (zeroProduct==zero); 210 | 211 | Complex nonzeroProduct = one*i; 212 | assert (nonzeroProduct==i); 213 | 214 | Complex diff = -one-zero-i; 215 | assert (diff==Complex(-1.0, -1.0)); 216 | 217 | Complex power = i^3; 218 | assert (power==Complex(-0.0, -1.0)); 219 | 220 | Complex quot = one/i; 221 | assert(quot==-i); 222 | 223 | } 224 | 225 | /* 226 | 227 | EXERCISE 228 | 229 | Write a Vector class, that supports vector 230 | addition with + (via the Summable interface) and 231 | scalar multiplication with ** (via the Scalable 232 | interface). 233 | 234 | */ -------------------------------------------------------------------------------- /source/en/07namedarguments.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet, 3 | HashMap 4 | } 5 | /* 6 | 7 | When a function has many parameters, it's 8 | better to list its arguments by name. Named 9 | argument lists are enclosed in braces, and 10 | individual arguments are separated by 11 | semicolons. 12 | 13 | */ 14 | 15 | shared void namedArgLists() { 16 | value entry1 = Entry { key = 1; item = "once"; }; 17 | value entry2 = Entry { item = "twice"; key = 2; }; 18 | value int1 = Integer.parse { string = "1000101"; radix = 2; }; 19 | value int2 = Integer.parse { radix = 16; string = "1000101"; }; 20 | print(entry1); 21 | print(entry2); 22 | print(int1); 23 | print(int2); 24 | } 25 | 26 | /* 27 | 28 | Even within a named argument list, we're 29 | allowed to list the first arguments 30 | positionally. (The reason for this will 31 | become clear below.) 32 | 33 | */ 34 | 35 | shared void namedArgListsWithPositionalArgs() { 36 | Entry { 1; item = "once"; }; 37 | Entry { 2; "twice"; }; 38 | Integer.parse { "1000101"; radix = 2; }; 39 | Integer.parse { "1000101"; 16; }; 40 | } 41 | 42 | /* 43 | 44 | At the end of a named argument list, we may 45 | list additional arguments, separated by 46 | commas, which are interpreted as arguments 47 | to the first parameter of type Iterable 48 | which does not already have an argument. 49 | 50 | This is the usual syntax we use for 51 | instantiating container types with an 52 | initially fixed list of elements. 53 | 54 | */ 55 | 56 | shared void namedArgListsWithIterableArgs() { 57 | value hello = String { 'H', 'e', 'l', 'l', 'o' }; 58 | value immutableSet = set { "once", "twice", "thrice" }; 59 | value hashSet = HashSet { 0, 1, -1 }; 60 | value hashMap = HashMap { 1->"once", 2->"twice", 3->"thrice", 0->"never" }; 61 | print(hello); 62 | print(immutableSet); 63 | print(hashSet); 64 | print(hashMap); 65 | } 66 | 67 | /* 68 | 69 | In Ceylon, anywhere we can write an 70 | arbitrary-length list of values, we can also 71 | write a comprehension or use the spread 72 | operator. 73 | 74 | */ 75 | 76 | shared void namedArgListsWithComprehensionArgs() { 77 | value hello = String { for (c in "HELLO") c.lowercased }; 78 | value immutableSet = set { "never", "once", "twice", "thrice" }; 79 | value hashSet = HashSet { *(-1..1) }; 80 | value hashMap = HashMap { *immutableSet.indexed }; 81 | print(hello); 82 | print(immutableSet); 83 | print(hashSet); 84 | print(hashMap); 85 | } 86 | 87 | /* 88 | 89 | We can pass a function as an argument using 90 | a very natural syntax. 91 | 92 | */ 93 | 94 | shared void namedFunctionalArg() { 95 | value iter = mapPairs { 96 | firstIterable = 1..5; 97 | secondIterable = { 98 | "once", 99 | "twice", 100 | "thrice", 101 | "four times", 102 | "five times" 103 | }; 104 | function collecting(Integer num, String word) 105 | => num -> word + " hello".repeat(num); 106 | }; 107 | print(iter); 108 | } 109 | 110 | /* 111 | 112 | All this seems like a lot of new syntax! But 113 | there's a deeper purpose behind it: named 114 | argument lists provide us with a very 115 | flexible syntax for defining tree-like 116 | structures. This has many applications, from 117 | build scripts to user interfaces. 118 | 119 | The following classes define the "schema" of 120 | a mini-language for defining tables. 121 | 122 | */ 123 | 124 | String center(String content, Integer size) { 125 | value padding = size-content.size; 126 | value paddingBefore = padding/2; 127 | value paddingAfter = padding-paddingBefore; 128 | return " ".repeat(paddingBefore) + content + 129 | " ".repeat(paddingAfter); 130 | } 131 | 132 | class Cell({String*} content) { 133 | shared actual String string { 134 | value result = StringBuilder(); 135 | for (s in content) { 136 | result.append(s); 137 | } 138 | return result.string; 139 | } 140 | } 141 | 142 | class Row({Cell*} cell) { 143 | shared Cell[] cells = cell.sequence(); 144 | shared actual String string { 145 | value result = StringBuilder(); 146 | result.append("|"); 147 | for (cell in cells) { 148 | result.append(center(cell.string, 45)); 149 | result.append("|"); 150 | } 151 | return result.string; 152 | } 153 | } 154 | 155 | class Table(String title, Row header, {Row*} rows) { 156 | shared actual String string { 157 | value result = StringBuilder(); 158 | value size = header.cells.size*46+1; 159 | result.append(center(title, size) + "\n"); 160 | result.append(center("-".repeat(title.size), size) + "\n"); 161 | result.append(header.string.replace("|", " ")+"\n"); 162 | result.append("-".repeat(size) + "\n"); 163 | for (row in rows) { 164 | result.append(row.string+"\n"); 165 | result.append("-".repeat(row.cells.size*46+1) + "\n"); 166 | } 167 | return result.string; 168 | } 169 | } 170 | 171 | /* 172 | 173 | Now we can define a table using a very 174 | natural syntax, where the code represents 175 | the structure of the table itself: 176 | 177 | */ 178 | 179 | Table table = Table { 180 | title = "Ceylon Project"; 181 | header = Row { 182 | Cell { "Module" }, 183 | Cell { "Description" }, 184 | Cell { "URL" } 185 | }; 186 | Row { 187 | Cell { "ceylon-spec" }, 188 | Cell { "The specification and typechecker" }, 189 | Cell { "https://github.com/ceylon/ceylon-spec" } 190 | }, 191 | Row { 192 | Cell { "ceylon-compiler" }, 193 | Cell { "The backend for the JVM" }, 194 | Cell { "https://github.com/ceylon/ceylon-compiler" } 195 | }, 196 | Row { 197 | Cell { "ceylon-js" }, 198 | Cell { "The backend for JavaScript" }, 199 | Cell { "https://github.com/ceylon/ceylon-js" } 200 | }, 201 | Row { 202 | Cell { "ceylon.language" }, 203 | Cell { "The language module" }, 204 | Cell { "https://github.com/ceylon/ceylon.language" } 205 | } 206 | }; 207 | 208 | shared void printTable() { 209 | print(table); 210 | } -------------------------------------------------------------------------------- /source/es/06interfaces_operadores.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Una interfaz define un contrato que puede ser 4 | satisfecho por una clase. Las interfaces pueden 5 | tener miembros formales, o incluso miembros 6 | concretos: 7 | 8 | - métodos, 9 | - getters y setters, y 10 | - clases miembro. 11 | 12 | Pero una interfaz no puede: 13 | 14 | - tener un atributo que mantenga una referencia 15 | a un valor, o 16 | - definir lógica de inicialización. 17 | 18 | Por tanto, decimos que las interfaces no tienen 19 | estado. Esto significa que la herencia "diamante" 20 | no es un problema en Ceylon. 21 | 22 | */ 23 | 24 | interface Writer { 25 | 26 | shared formal void write(String string); 27 | 28 | shared void writeLine(String string) { 29 | write(string + operatingSystem.newline); 30 | } 31 | 32 | } 33 | 34 | /* 35 | 36 | Una clase puede satisfacer la interfaz, 37 | heredando sus miembros. 38 | 39 | */ 40 | 41 | class FunctionWriter(void fun(String string)) 42 | satisfies Writer { 43 | write = fun; 44 | } 45 | 46 | shared void testFunctionWriter() { 47 | FunctionWriter(process.write).writeLine("Hello!"); 48 | FunctionWriter(process.writeError).writeLine("Ooops!"); 49 | } 50 | 51 | /* 52 | 53 | Una clase puede satisfacer múltiples interfaces. 54 | Usamos el símbolo & para separar los supertipos 55 | porque conceptualmente forman una intersección 56 | de tipos. 57 | 58 | */ 59 | 60 | class TextWriter(StringBuilder stringBuilder) 61 | satisfies Writer & Category<> { 62 | 63 | contains(Object element) 64 | => element in stringBuilder.string; 65 | 66 | write(String string) 67 | => stringBuilder.append(string); 68 | 69 | } 70 | 71 | shared void testTextWriter() { 72 | 73 | value textWriter = TextWriter(StringBuilder()); 74 | textWriter.writeLine("Hello :-)"); 75 | textWriter.writeLine("Goodbye :-("); 76 | 77 | // "x in category" significa "category.contains(x)" 78 | // donde category es una instancia de Category 79 | assert(":-)" in textWriter); 80 | 81 | } 82 | 83 | /* 84 | 85 | Es especialmente común tener clases anónimas 86 | que satisfacen una interfaz. 87 | 88 | */ 89 | 90 | object consoleWriter satisfies Writer { 91 | write = process.write; 92 | } 93 | 94 | shared void testConsoleWriter() { 95 | consoleWriter.writeLine("Hello!"); 96 | consoleWriter.writeLine("Bye!"); 97 | } 98 | 99 | /* 100 | 101 | La clase anónima de arriba es un singleton, 102 | porque está declarada en el primer nivel. Pero 103 | no todas las clases anónimas son singleton. 104 | 105 | */ 106 | 107 | //esta clase anónima es un singleton 108 | object naturals 109 | satisfies Iterable { 110 | 111 | shared actual Iterator iterator() { 112 | //una nueva instancia de esta clase 113 | //anónima se crea cada vez que se llama 114 | //a iterator() 115 | object iterator 116 | satisfies Iterator { 117 | variable value int = 1; 118 | next() => int++; 119 | } 120 | return iterator; 121 | } 122 | 123 | } 124 | 125 | /* 126 | 127 | La clase anónima de arriba satisface Iterable. 128 | Esto significa que podemos iterarla con un 129 | bucle for. 130 | 131 | */ 132 | 133 | shared void loop() { 134 | for (n in naturals) { 135 | print(n); 136 | } 137 | } 138 | 139 | /* 140 | 141 | Muchas de las construcciones intrínsecas al 142 | lenguaje se definen en términos de interfaces 143 | que tus propias clases pueden implementar. 144 | En particular, la mayoría de los operadores se 145 | definen en términos de interfaces. 146 | 147 | Veamos como podemos definir un tipo "número 148 | complejo" que funcione exactamente igual que 149 | los tipos numéricos intrínsecos. 150 | 151 | */ 152 | 153 | "A complex number class demonstrating operator 154 | polymorphism in Ceylon." 155 | class Complex(shared Float re, shared Float im=0.0) 156 | satisfies Exponentiable { 157 | 158 | negated => Complex(-re,-im); 159 | 160 | plus(Complex other) => Complex(re+other.re, im+other.im); 161 | 162 | minus(Complex other) => Complex(re-other.re, im-other.im); 163 | 164 | times(Complex other) => 165 | Complex(re*other.re-im*other.im, 166 | re*other.im+im*other.re); 167 | 168 | shared actual Complex divided(Complex other) { 169 | Float d = other.re^2 + other.im^2; 170 | return Complex((re*other.re+im*other.im)/d, 171 | (im*other.re-re*other.im)/d); 172 | } 173 | 174 | "Accepts non-negative powers." 175 | shared actual Complex power(Integer other) { 176 | "exponent must be non-negative" 177 | assert(other>=0); 178 | //impl pobre 179 | variable Complex result = Complex(1.0, 0.0); 180 | for (i in 0:other) { 181 | result*=this; 182 | } 183 | return result; 184 | } 185 | 186 | string => im<0.0 then "``re``-``-im``i" 187 | else "``re``+``im``i"; 188 | 189 | hash => re.hash + im.hash; 190 | 191 | shared actual Boolean equals(Object that) { 192 | if (is Complex that) { 193 | return re==that.re && im==that.im; 194 | } 195 | else { 196 | return false; 197 | } 198 | } 199 | 200 | } 201 | 202 | Complex i = Complex(0.0, 1.0); 203 | 204 | shared void testComplex() { 205 | 206 | Complex one = Complex(1.0); 207 | Complex zero = Complex(0.0); 208 | 209 | Complex sum = one+i+zero; 210 | assert (sum==Complex(1.0, 1.0)); 211 | 212 | Complex zeroProduct = one*zero; 213 | assert (zeroProduct==zero); 214 | 215 | Complex nonzeroProduct = one*i; 216 | assert (nonzeroProduct==i); 217 | 218 | Complex diff = -one-zero-i; 219 | assert (diff==Complex(-1.0, -1.0)); 220 | 221 | Complex power = i^3; 222 | assert (power==Complex(-0.0, -1.0)); 223 | 224 | Complex quot = one/i; 225 | assert(quot==-i); 226 | 227 | } 228 | 229 | /* 230 | 231 | EJERCICIO 232 | 233 | Escribe una clase Vector, que soporte suma de 234 | vectores con + (a través de la interfaz 235 | Summable) y producto escalar con ** (a través 236 | de la interfaz Scalable). 237 | 238 | */ -------------------------------------------------------------------------------- /source/es/07argumentos_con_nombre.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet, 3 | HashMap 4 | } 5 | /* 6 | 7 | Cuando una función tiene muchos parámetros, es 8 | mejor listar sus argumentos por nombre. Los 9 | argumentos con nombre van rodeados por llaves, 10 | y cada argumento individual se separa por punto 11 | y coma. 12 | 13 | */ 14 | 15 | shared void namedArgLists() { 16 | value entry1 = Entry { key = 1; item = "once"; }; 17 | value entry2 = Entry { item = "twice"; key = 2; }; 18 | value int1 = Integer.parse { string = "1000101"; radix = 2; }; 19 | value int2 = Integer.parse { radix = 16; string = "1000101"; }; 20 | print(entry1); 21 | print(entry2); 22 | print(int1); 23 | print(int2); 24 | } 25 | 26 | /* 27 | 28 | Incluso dentro de una lista de argumentos con 29 | nombre, se permite listar los primeros 30 | argumentos posicionalmente. (La razón para esto 31 | quedará más clara más abajo.) 32 | 33 | */ 34 | 35 | shared void namedArgListsWithPositionalArgs() { 36 | Entry { 1; item = "once"; }; 37 | Entry { 2; "twice"; }; 38 | Integer.parse { "1000101"; radix = 2; }; 39 | Integer.parse { "1000101"; 16; }; 40 | } 41 | 42 | /* 43 | 44 | Al final de una lista de argumentos con nombre, 45 | podemos listar argumentos adicionales, separados 46 | por comas, que se interpretan como argumentos al 47 | primer parámetro de tipo Iterable que no tenga 48 | ya un argumento. 49 | 50 | Esta es la sintaxis que habitualmente usamos para 51 | instanciar tipos contenedor con una lista fija 52 | inicial de elementos. 53 | 54 | */ 55 | 56 | shared void namedArgListsWithIterableArgs() { 57 | value hello = String { 'H', 'e', 'l', 'l', 'o' }; 58 | value immutableSet = set { "once", "twice", "thrice" }; 59 | value hashSet = HashSet { 0, 1, -1 }; 60 | value hashMap = HashMap { 1->"once", 2->"twice", 3->"thrice", 0->"never" }; 61 | print(hello); 62 | print(immutableSet); 63 | print(hashSet); 64 | print(hashMap); 65 | } 66 | 67 | /* 68 | 69 | En Ceylon, donde se pueda escribir una lista de 70 | valores de longitud arbitraria, también se puede 71 | escribir una comprensión o utilizar el operador 72 | desplegar. 73 | 74 | */ 75 | 76 | shared void namedArgListsWithComprehensionArgs() { 77 | value hello = String { for (c in "HELLO") c.lowercased }; 78 | value immutableSet = set { "never", "once", "twice", "thrice" }; 79 | value hashSet = HashSet { *(-1..1) }; 80 | value hashMap = HashMap { *immutableSet.indexed }; 81 | print(hello); 82 | print(immutableSet); 83 | print(hashSet); 84 | print(hashMap); 85 | } 86 | 87 | /* 88 | 89 | Podemos pasar una función como un argumento 90 | utilizando una sintaxis muy natural. 91 | 92 | */ 93 | 94 | shared void namedFunctionalArg() { 95 | value iter = mapPairs { 96 | firstIterable = 1..5; 97 | secondIterable = { 98 | "once", 99 | "twice", 100 | "thrice", 101 | "four times", 102 | "five times" 103 | }; 104 | function collecting(Integer num, String word) 105 | => num -> word + " hello".repeat(num); 106 | }; 107 | print(iter); 108 | } 109 | 110 | /* 111 | 112 | ¡Todo esto parece un montón de nueva sintaxis! 113 | Pero hay un propósito más profundo detrás de 114 | ello: las listas de argumentos con nombre nos 115 | proporcionan una sintaxis muy flexible para 116 | definir estructuras con forma de árbol. Esto 117 | tiene muchas aplicaciones, desde scripts de 118 | construcción a interfaces de usuario. 119 | 120 | Las siguientes clases definen el "esquema" de 121 | un mini-lenguaje para definir tablas. 122 | 123 | */ 124 | 125 | String center(String content, Integer size) { 126 | value padding = size-content.size; 127 | value paddingBefore = padding/2; 128 | value paddingAfter = padding-paddingBefore; 129 | return " ".repeat(paddingBefore) + content + 130 | " ".repeat(paddingAfter); 131 | } 132 | 133 | class Cell({String*} content) { 134 | shared actual String string { 135 | value result = StringBuilder(); 136 | for (s in content) { 137 | result.append(s); 138 | } 139 | return result.string; 140 | } 141 | } 142 | 143 | class Row({Cell*} cell) { 144 | shared Cell[] cells = cell.sequence(); 145 | shared actual String string { 146 | value result = StringBuilder(); 147 | result.append("|"); 148 | for (cell in cells) { 149 | result.append(center(cell.string, 45)); 150 | result.append("|"); 151 | } 152 | return result.string; 153 | } 154 | } 155 | 156 | class Table(String title, Row header, {Row*} rows) { 157 | shared actual String string { 158 | value result = StringBuilder(); 159 | value size = header.cells.size*46+1; 160 | result.append(center(title, size) + "\n"); 161 | result.append(center("-".repeat(title.size), size) + "\n"); 162 | result.append(header.string.replace("|", " ")+"\n"); 163 | result.append("-".repeat(size) + "\n"); 164 | for (row in rows) { 165 | result.append(row.string+"\n"); 166 | result.append("-".repeat(row.cells.size*46+1) + "\n"); 167 | } 168 | return result.string; 169 | } 170 | } 171 | 172 | /* 173 | 174 | Ahora podemos definir una tabla utilizando una 175 | sintaxis muy natural, donde el código 176 | representa la estructura de la tabla en sí: 177 | 178 | */ 179 | 180 | Table table = Table { 181 | title = "Ceylon Project"; 182 | header = Row { 183 | Cell { "Module" }, 184 | Cell { "Description" }, 185 | Cell { "URL" } 186 | }; 187 | Row { 188 | Cell { "ceylon-spec" }, 189 | Cell { "The specification and typechecker" }, 190 | Cell { "https://github.com/ceylon/ceylon-spec" } 191 | }, 192 | Row { 193 | Cell { "ceylon-compiler" }, 194 | Cell { "The backend for the JVM" }, 195 | Cell { "https://github.com/ceylon/ceylon-compiler" } 196 | }, 197 | Row { 198 | Cell { "ceylon-js" }, 199 | Cell { "The backend for JavaScript" }, 200 | Cell { "https://github.com/ceylon/ceylon-js" } 201 | }, 202 | Row { 203 | Cell { "ceylon.language" }, 204 | Cell { "The language module" }, 205 | Cell { "https://github.com/ceylon/ceylon.language" } 206 | } 207 | }; 208 | 209 | shared void printTable() { 210 | print(table); 211 | } -------------------------------------------------------------------------------- /source/fr/06interfaces_operateurs.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Une interface définie un contrat qui peut être 4 | satisfait par une classe. Les interfaces peuvent 5 | avoir des membres formels, ou même des membres 6 | concrets : 7 | 8 | - méthodes, 9 | - getters et setters, et 10 | - classes membres. 11 | 12 | Mais une interface ne peux pas : 13 | 14 | - avoir un attribut qui conserve une référence 15 | sur une valeur, ou 16 | - définir une logique d'initialisation. 17 | 18 | Ainsi, nous pouvons dire que les interfaces 19 | sont sans état. Cela signifie que les héritages 20 | en "diamant" ne sont pas un problème en Ceylon. 21 | 22 | */ 23 | 24 | interface Writer { 25 | 26 | shared formal void write(String string); 27 | 28 | shared void writeLine(String string) { 29 | write(string + operatingSystem.newline); 30 | } 31 | 32 | } 33 | 34 | /* 35 | 36 | Une classe peut satisfaire une interface, 37 | héritant alors de ses membres. 38 | 39 | */ 40 | 41 | class FunctionWriter(void fun(String string)) 42 | satisfies Writer { 43 | write = fun; 44 | } 45 | 46 | shared void testFunctionWriter() { 47 | FunctionWriter(process.write).writeLine("Hello!"); 48 | FunctionWriter(process.writeError).writeLine("Ooops!"); 49 | } 50 | 51 | /* 52 | 53 | Une classe peut satisfaire plusieurs interfaces. 54 | Nous utilisons le symbole & pour séparer les 55 | supertypes car conceptuellement ils forment 56 | une intersection de types. 57 | 58 | */ 59 | 60 | class TextWriter(StringBuilder stringBuilder) 61 | satisfies Writer & Category<> { 62 | 63 | contains(Object element) 64 | => element in stringBuilder.string; 65 | 66 | write(String string) 67 | => stringBuilder.append(string); 68 | 69 | } 70 | 71 | shared void testTextWriter() { 72 | 73 | value textWriter = TextWriter(StringBuilder()); 74 | textWriter.writeLine("Hello :-)"); 75 | textWriter.writeLine("Goodbye :-("); 76 | 77 | // "x in category" signifie "category.contains(x)" 78 | // où category est une instance de Category 79 | assert(":-)" in textWriter); 80 | 81 | } 82 | 83 | /* 84 | 85 | Il est particulièrement répandu d'avoir des 86 | classes anonymes qui satisfont une interface. 87 | 88 | */ 89 | 90 | object consoleWriter satisfies Writer { 91 | write = process.write; 92 | } 93 | 94 | shared void testConsoleWriter() { 95 | consoleWriter.writeLine("Hello!"); 96 | consoleWriter.writeLine("Bye!"); 97 | } 98 | 99 | /* 100 | 101 | La classe anonyme ci-dessus est un singleton, 102 | car sa déclaration est au premier niveau. 103 | Néanmoins, toutes les classes anonymes ne sont 104 | pas des singletons. 105 | 106 | */ 107 | 108 | //cette classe anonyme est un singleton 109 | object naturals 110 | satisfies Iterable { 111 | 112 | shared actual Iterator iterator() { 113 | //une nouvelle instance de cette classes 114 | //anonyme est créée à chaque fois que 115 | //iterator() est appelé 116 | object iterator 117 | satisfies Iterator { 118 | variable value int = 1; 119 | next() => int++; 120 | } 121 | return iterator; 122 | } 123 | 124 | } 125 | 126 | /* 127 | 128 | La classe anonyme ci-dessus satisfait Iterable. 129 | Nous pouvons donc l'itérer avec une boucle for. 130 | 131 | */ 132 | 133 | shared void loop() { 134 | for (n in naturals) { 135 | print(n); 136 | } 137 | } 138 | 139 | /* 140 | 141 | De nombreuses constructions syntaxiques intégrées 142 | au langage sont définies via des interfaces 143 | que vos propres classes peuvent implémenter. 144 | En particulier, la plupart des opérateurs 145 | sont définis en termes d'interfaces. 146 | 147 | Voyons comment définir un type de nombre 148 | complexe qui fonctionne de la même manière 149 | que les types numériques fournis par le 150 | langage. 151 | 152 | */ 153 | 154 | "Une classes de nombre complexe illustrant le 155 | polymorphisme d'opérateur en Ceylon" 156 | class Complex(shared Float re, shared Float im=0.0) 157 | satisfies Exponentiable { 158 | 159 | negated => Complex(-re,-im); 160 | 161 | plus(Complex other) => Complex(re+other.re, im+other.im); 162 | 163 | minus(Complex other) => Complex(re-other.re, im-other.im); 164 | 165 | times(Complex other) => 166 | Complex(re*other.re-im*other.im, 167 | re*other.im+im*other.re); 168 | 169 | shared actual Complex divided(Complex other) { 170 | Float d = other.re^2 + other.im^2; 171 | return Complex((re*other.re+im*other.im)/d, 172 | (im*other.re-re*other.im)/d); 173 | } 174 | 175 | "Accepte des puissances non-négatives" 176 | shared actual Complex power(Integer other) { 177 | "L'exposant doit être non-négatif" 178 | assert(other>=0); 179 | //impl simpliste 180 | variable Complex result = Complex(1.0, 0.0); 181 | for (i in 0:other) { 182 | result*=this; 183 | } 184 | return result; 185 | } 186 | 187 | string => im<0.0 then "``re``-``-im``i" 188 | else "``re``+``im``i"; 189 | 190 | hash => re.hash + im.hash; 191 | 192 | shared actual Boolean equals(Object that) { 193 | if (is Complex that) { 194 | return re==that.re && im==that.im; 195 | } 196 | else { 197 | return false; 198 | } 199 | } 200 | 201 | } 202 | 203 | Complex i = Complex(0.0, 1.0); 204 | 205 | shared void testComplex() { 206 | 207 | Complex one = Complex(1.0); 208 | Complex zero = Complex(0.0); 209 | 210 | Complex sum = one+i+zero; 211 | assert (sum==Complex(1.0, 1.0)); 212 | 213 | Complex zeroProduct = one*zero; 214 | assert (zeroProduct==zero); 215 | 216 | Complex nonzeroProduct = one*i; 217 | assert (nonzeroProduct==i); 218 | 219 | Complex diff = -one-zero-i; 220 | assert (diff==Complex(-1.0, -1.0)); 221 | 222 | Complex power = i^3; 223 | assert (power==Complex(-0.0, -1.0)); 224 | 225 | Complex quot = one/i; 226 | assert(quot==-i); 227 | 228 | } 229 | 230 | /* 231 | 232 | EXERCICE 233 | 234 | Ecrire une classe Vector, qui supporte l'addition vectorielle 235 | avec + (via l'interface Summable) et le produit scalaire 236 | avec ** (via l'interface Scalable). 237 | 238 | */ -------------------------------------------------------------------------------- /source/de/07benannte_argumente.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet, 3 | HashMap 4 | } 5 | /* 6 | 7 | Wenn eine Funktion viele Parameter hat, ist es 8 | oft besser, ihre Argumente mit Namen versehen 9 | zu notieren. Listen benannter Argumente werden 10 | in geschweifen Klammern eingeschlossen, und die 11 | einzelnen Argumente sind durch Strichpunkte 12 | getrennt. 13 | 14 | */ 15 | 16 | shared void namedArgLists() { 17 | value entry1 = Entry { key = 1; item = "einmal"; }; 18 | value entry2 = Entry { item = "zweimal"; key = 2; }; 19 | value int1 = Integer.parse { string = "1000101"; radix = 2; }; 20 | value int2 = Integer.parse { radix = 16; string = "1000101"; }; 21 | print(entry1); 22 | print(entry2); 23 | print(int1); 24 | print(int2); 25 | } 26 | 27 | /* 28 | 29 | Selbst innerhalb einer Liste benannter Argumente 30 | dürfen wir die ersten Argumente nur nach ihrer 31 | Position auflisten. (Der Grund dafür wird gleich 32 | klar.) 33 | 34 | */ 35 | 36 | shared void namedArgListsWithPositionalArgs() { 37 | Entry { 1; item = "einmal"; }; 38 | Entry { 2; "zweimal"; }; 39 | Integer.parse { "1000101"; radix = 2; }; 40 | Integer.parse { "1000101"; 16; }; 41 | } 42 | 43 | /* 44 | 45 | Am Ende einer Liste benannter Argumente dürfen 46 | wir zusätzliche Argumente angeben, durch Kommas 47 | getrennt, die als Argumente für den ersten 48 | Parameter vom Typ Iterable, der noch kein 49 | Argument hat, gelten. 50 | 51 | Das ist die Syntax, die wir meist verwenden, 52 | um Container-Typen mit einer anfangs festen 53 | Menge an Elementen zu instanziieren. 54 | 55 | */ 56 | 57 | shared void namedArgListsWithIterableArgs() { 58 | value hallo = String { 'H', 'a', 'l', 'l', 'o' }; 59 | value immutableSet = set { "einmal", "zweimal", "dreimal" }; 60 | value hashSet = HashSet { 0, 1, -1 }; 61 | value hashMap = HashMap { 1->"einmal", 2->"zweimal", 3->"dreimal", 0->"nie" }; 62 | print(hallo); 63 | print(immutableSet); 64 | print(hashSet); 65 | print(hashMap); 66 | } 67 | 68 | /* 69 | 70 | In Ceylon können wir überall, wo wir eine 71 | beliebig lange Liste von Werten angeben können, 72 | auch eine Comprehension oder den Verteilungs- 73 | Operator verwenden. 74 | 75 | */ 76 | 77 | shared void namedArgListsWithComprehensionArgs() { 78 | value hallo = String { for (c in "HALLO") c.lowercased }; 79 | value immutableSet = set { "nie", "einmal", "zweimal", "dreimal" }; 80 | value hashSet = HashSet { *(-1..1) }; 81 | value hashMap = HashMap { *immutableSet.indexed }; 82 | print(hallo); 83 | print(immutableSet); 84 | print(hashSet); 85 | print(hashMap); 86 | } 87 | 88 | /* 89 | 90 | Mit der folgenden, sehr natürlichen Syntax können 91 | wir eine Funktion als Argument übergeben. 92 | 93 | */ 94 | 95 | shared void namedFunctionalArg() { 96 | value iter = mapPairs { 97 | firstIterable = 1..5; 98 | secondIterable = { 99 | "einmal", 100 | "zweimal", 101 | "dreimal", 102 | "viermal", 103 | "fünfmal" 104 | }; 105 | function collecting(Integer num, String word) 106 | => num -> word + " Hallo".repeat(num); 107 | }; 108 | print(iter); 109 | } 110 | 111 | /* 112 | 113 | Das sieht wie eine Menge neuer Syntax aus! Aber 114 | es gibt einen tieferen Sinn dahinter: Listen 115 | benannter Argument geben uns eine sehr flexible 116 | Syntax, um baumartige Strukturen zu definieren. 117 | Dafür gibt es viele Anwendungen, von Build- 118 | Skripten bis zu Benutzeroberflächen. 119 | 120 | Die folgenden Klassen definieren das "Schema" für 121 | eine Mini-Sprache, um Tabellen zu definieren. 122 | 123 | */ 124 | 125 | String center(String content, Integer size) { 126 | value padding = size-content.size; 127 | value paddingBefore = padding/2; 128 | value paddingAfter = padding-paddingBefore; 129 | return " ".repeat(paddingBefore) + content + 130 | " ".repeat(paddingAfter); 131 | } 132 | 133 | class Cell({String*} content) { 134 | shared actual String string { 135 | value result = StringBuilder(); 136 | for (s in content) { 137 | result.append(s); 138 | } 139 | return result.string; 140 | } 141 | } 142 | 143 | class Row({Cell*} cell) { 144 | shared Cell[] cells = cell.sequence(); 145 | shared actual String string { 146 | value result = StringBuilder(); 147 | result.append("|"); 148 | for (cell in cells) { 149 | result.append(center(cell.string, 45)); 150 | result.append("|"); 151 | } 152 | return result.string; 153 | } 154 | } 155 | 156 | class Table(String title, Row header, {Row*} rows) { 157 | shared actual String string { 158 | value result = StringBuilder(); 159 | value size = header.cells.size*46+1; 160 | result.append(center(title, size) + "\n"); 161 | result.append(center("-".repeat(title.size), size) + "\n"); 162 | result.append(header.string.replace("|", " ")+"\n"); 163 | result.append("-".repeat(size) + "\n"); 164 | for (row in rows) { 165 | result.append(row.string+"\n"); 166 | result.append("-".repeat(row.cells.size*46+1) + "\n"); 167 | } 168 | return result.string; 169 | } 170 | } 171 | 172 | /* 173 | 174 | Jetzt können wir eine Tabelle mit einer sehr 175 | natürlichen Syntax definieren, wobei der Code 176 | die Struktur der Tabelle selbst darstellt: 177 | 178 | */ 179 | 180 | Table table = Table { 181 | title = "Ceylon-Project"; 182 | header = Row { 183 | Cell { "Modul" }, 184 | Cell { "Beschreibung" }, 185 | Cell { "URL" } 186 | }; 187 | Row { 188 | Cell { "ceylon-spec" }, 189 | Cell { "Die Spezifikation und der Typechecker" }, 190 | Cell { "https://github.com/ceylon/ceylon-spec" } 191 | }, 192 | Row { 193 | Cell { "ceylon-compiler" }, 194 | Cell { "Das Backend für die JVM" }, 195 | Cell { "https://github.com/ceylon/ceylon-compiler" } 196 | }, 197 | Row { 198 | Cell { "ceylon-js" }, 199 | Cell { "Das Backend für JavaScript" }, 200 | Cell { "https://github.com/ceylon/ceylon-js" } 201 | }, 202 | Row { 203 | Cell { "ceylon.language" }, 204 | Cell { "Das Sprachmodul" }, 205 | Cell { "https://github.com/ceylon/ceylon.language" } 206 | } 207 | }; 208 | 209 | shared void printTable() { 210 | print(table); 211 | } 212 | -------------------------------------------------------------------------------- /source/de/06schnittstellen_operatoren.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Eine Schnittstelle (interface) definiert einen 4 | Vertrag, der von einer Klasse erfüllt werden 5 | kann. Schnittstellen können Member definieren, 6 | die "formal" annotiert sind, oder sogar konkrete 7 | Member: 8 | 9 | - Methoden, 10 | - Getter und Setter, und 11 | - Member-Klassen. 12 | 13 | Aber eine Schnittstelle kann nicht 14 | 15 | - ein Attribut, das einen Verweis auf einen Wert 16 | enthält, haben, oder 17 | - Initialisierungslogik definieren. 18 | 19 | Deshalb nennen wir Schnittstellen zustandslos. 20 | Das bedeutet, dass "Rauten-Vererbung" in Ceylon 21 | kein Problem darstellt. 22 | 23 | */ 24 | 25 | interface Writer { 26 | 27 | shared formal void write(String string); 28 | 29 | shared void writeLine(String string) { 30 | write(string + operatingSystem.newline); 31 | } 32 | 33 | } 34 | 35 | /* 36 | 37 | Eine Klasse kann eine Schnittstelle erfüllen und 38 | ihre Member erben. 39 | 40 | */ 41 | 42 | class FunctionWriter(void fun(String string)) 43 | satisfies Writer { 44 | write = fun; 45 | } 46 | 47 | shared void testFunctionWriter() { 48 | FunctionWriter(process.write).writeLine("Hallo!"); 49 | FunctionWriter(process.writeError).writeLine("Uups!"); 50 | } 51 | 52 | /* 53 | 54 | Eine Klasse kann mehrere Schnittstellen erfüllen. 55 | Wir verwenden das '&'-Symbol, um die erfüllten 56 | Schnittstellen zu trennen, weil sie prinzipiell 57 | einen Schnitttyp darstellen. 58 | 59 | */ 60 | 61 | class TextWriter(StringBuilder stringBuilder) 62 | satisfies Writer & Category<> { 63 | 64 | contains(Object element) 65 | => element in stringBuilder.string; 66 | 67 | write(String string) 68 | => stringBuilder.append(string); 69 | 70 | } 71 | 72 | shared void testTextWriter() { 73 | 74 | value textWriter = TextWriter(StringBuilder()); 75 | textWriter.writeLine("Hallo :-)"); 76 | textWriter.writeLine("Tschüss :-("); 77 | 78 | // "x in category" heißt "category.contains(x)" 79 | // wobei category eine Instanz von Category ist 80 | assert(":-)" in textWriter); 81 | 82 | } 83 | 84 | /* 85 | 86 | Es ist besonders häufig, anonyme Klassen zu haben, 87 | die eine Schnittstelle erfüllen. 88 | 89 | */ 90 | 91 | object consoleWriter satisfies Writer { 92 | write = process.write; 93 | } 94 | 95 | shared void testConsoleWriter() { 96 | consoleWriter.writeLine("Hallo!"); 97 | consoleWriter.writeLine("Tschüss!"); 98 | } 99 | 100 | /* 101 | 102 | Die obige anonyme Klasse ist ein Singleton 103 | (Einweg-Objekt), weil die Deklaration auf oberster 104 | Ebene steht und nicht verschachtelt ist. Aber 105 | nicht jede anonyme Klasse ist ein Singleton. 106 | 107 | */ 108 | 109 | // Diese anonyme Klasse ist ein Singleton 110 | object naturals 111 | satisfies Iterable { 112 | 113 | shared actual Iterator iterator() { 114 | // Jedes Mal, wenn iterator() aufgerufen 115 | // wird, wird eine neue Instanz dieser 116 | // anonymen Klasse erstellt 117 | object iterator 118 | satisfies Iterator { 119 | variable value int = 1; 120 | next() => int++; 121 | } 122 | return iterator; 123 | } 124 | 125 | } 126 | 127 | /* 128 | 129 | Die obige anonyme Klasse erfüllt Iterable. 130 | Damit können wir sie mit einer for-Schleife 131 | durchlaufen. 132 | 133 | */ 134 | 135 | shared void loop() { 136 | for (n in naturals) { 137 | print(n); 138 | } 139 | } 140 | 141 | /* 142 | 143 | Viele der "eingebauten" Sprachkonstrukte sind 144 | über Schnittstellen definiert, die deine eigenen 145 | Klassen erfüllen können. Insbesondere sind fast 146 | alle Operatoren über Schnittstellen definiert. 147 | 148 | Schauen wir mal, wie wir einen Typ für komplexe 149 | Zahlen definieren können, der genauso wie die 150 | eingebauten numerischen Typen funktioniert. 151 | 152 | */ 153 | 154 | "Eine Klasse für komplexe Zahlen, die 155 | Operatorpolymorphismus in Ceylon demonstriert." 156 | class Complex(shared Float re, shared Float im=0.0) 157 | satisfies Exponentiable { 158 | 159 | negated => Complex(-re,-im); 160 | 161 | plus(Complex other) => Complex(re+other.re, im+other.im); 162 | 163 | minus(Complex other) => Complex(re-other.re, im-other.im); 164 | 165 | times(Complex other) => 166 | Complex(re*other.re-im*other.im, 167 | re*other.im+im*other.re); 168 | 169 | shared actual Complex divided(Complex other) { 170 | Float d = other.re^2 + other.im^2; 171 | return Complex((re*other.re+im*other.im)/d, 172 | (im*other.re-re*other.im)/d); 173 | } 174 | 175 | "Akzeptiert nichtnegative Exponenten." 176 | shared actual Complex power(Integer other) { 177 | "Exponent darf nicht negativ sein" 178 | assert(other>=0); 179 | // einfache, langsame Implementierung 180 | variable Complex result = Complex(1.0, 0.0); 181 | for (i in 0:other) { 182 | result*=this; 183 | } 184 | return result; 185 | } 186 | 187 | string => im<0.0 then "``re``-``-im``i" 188 | else "``re``+``im``i"; 189 | 190 | hash => re.hash + im.hash; 191 | 192 | shared actual Boolean equals(Object that) { 193 | if (is Complex that) { 194 | return re==that.re && im==that.im; 195 | } 196 | else { 197 | return false; 198 | } 199 | } 200 | 201 | } 202 | 203 | Complex i = Complex(0.0, 1.0); 204 | 205 | shared void testComplex() { 206 | 207 | Complex one = Complex(1.0); 208 | Complex zero = Complex(0.0); 209 | 210 | Complex sum = one+i+zero; 211 | assert (sum==Complex(1.0, 1.0)); 212 | 213 | Complex zeroProduct = one*zero; 214 | assert (zeroProduct==zero); 215 | 216 | Complex nonzeroProduct = one*i; 217 | assert (nonzeroProduct==i); 218 | 219 | Complex diff = -one-zero-i; 220 | assert (diff==Complex(-1.0, -1.0)); 221 | 222 | Complex power = i^3; 223 | assert (power==Complex(-0.0, -1.0)); 224 | 225 | Complex quot = one/i; 226 | assert(quot==-i); 227 | 228 | } 229 | 230 | /* 231 | 232 | ÜBUNG 233 | 234 | Schreibe eine Vektor-Klasse, die Vektoraddition 235 | mit + (über die Summable-Schnittstelle) und 236 | skalare Multiplikation mit ** (über die Scalable- 237 | Schnittstelle) unterstützt. 238 | 239 | */ 240 | -------------------------------------------------------------------------------- /source/fr/07arguments_nommes.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashMap, 3 | HashSet 4 | } 5 | /* 6 | 7 | Quand une fonction a de nombreux paramètres, 8 | il vaut mieux lister ses arguments par nom. 9 | Les listes d'arguments nommés sont encadrées 10 | par des accolades, et les arguments eux même 11 | sont séparés par des points-virgules. 12 | 13 | */ 14 | 15 | shared void namedArgLists() { 16 | value entry1 = Entry { key = 1; item = "once"; }; 17 | value entry2 = Entry { item = "twice"; key = 2; }; 18 | value int1 = Integer.parse { string = "1000101"; radix = 2; }; 19 | value int2 = Integer.parse { radix = 16; string = "1000101"; }; 20 | print(entry1); 21 | print(entry2); 22 | print(int1); 23 | print(int2); 24 | } 25 | 26 | /* 27 | 28 | Même à l'intérieur d'une liste d'arguments 29 | nommés, nous pouvons lister le premier 30 | élément de manière positionnelle. (La raison 31 | de cela sera explicité ci-dessous) 32 | 33 | */ 34 | 35 | shared void namedArgListsWithPositionalArgs() { 36 | Entry { 1; item = "once"; }; 37 | Entry { 2; "twice"; }; 38 | Integer.parse { "1000101"; radix = 2; }; 39 | Integer.parse { "1000101"; 16; }; 40 | } 41 | 42 | /* 43 | 44 | A la fin d'une liste d'arguments nommés, 45 | nous pouvons lister des arguments 46 | additionnels, séparés par des virgules, 47 | qui sont interprétés en tant qu'arguments 48 | du premier paramètre de type Iterable qui 49 | n'a pas déjà un argument. 50 | 51 | C'est la syntaxe habituelle que nous 52 | utilisons pour instancier les types de 53 | container avec une liste initiale 54 | d'éléments. 55 | 56 | */ 57 | 58 | shared void namedArgListsWithIterableArgs() { 59 | value hello = String { 'H', 'e', 'l', 'l', 'o' }; 60 | value immutableSet = set { "once", "twice", "thrice" }; 61 | value hashSet = HashSet { 0, 1, -1 }; 62 | value hashMap = HashMap { 1->"once", 2->"twice", 3->"thrice", 0->"never" }; 63 | print(hello); 64 | print(immutableSet); 65 | print(hashSet); 66 | print(hashMap); 67 | } 68 | 69 | /* 70 | 71 | En Ceylon, aux endroits où nous pouvons 72 | écrire une liste de valeurs de longueur 73 | arbitraire, nous pouvons également écrire 74 | une compréhension ou utiliser l'opérateur 75 | d'expansion. 76 | 77 | */ 78 | 79 | shared void namedArgListsWithComprehensionArgs() { 80 | value hello = String { for (c in "HELLO") c.lowercased }; 81 | value immutableSet = set { "never", "once", "twice", "thrice" }; 82 | value hashSet = HashSet { *(-1..1) }; 83 | value hashMap = HashMap { *immutableSet.indexed }; 84 | print(hello); 85 | print(immutableSet); 86 | print(hashSet); 87 | print(hashMap); 88 | } 89 | 90 | /* 91 | 92 | Nous pouvons passer une fonction en tant 93 | qu'argument via une syntaxe très naturelle. 94 | 95 | */ 96 | 97 | shared void namedFunctionalArg() { 98 | value iter = mapPairs { 99 | firstIterable = 1..5; 100 | secondIterable = { 101 | "once", 102 | "twice", 103 | "thrice", 104 | "four times", 105 | "five times" 106 | }; 107 | function collecting(Integer num, String word) 108 | => num -> word + " hello".repeat(num); 109 | }; 110 | print(iter); 111 | } 112 | 113 | /* 114 | 115 | Tout ceci semble représenter beaucoup de 116 | nouvelles syntaxes! Mais il y a un but plus 117 | profond derrière ceci : les listes 118 | d'arguments nommés nous fournissent un 119 | moyen extrêmement flexible pour définir 120 | des structures d'arbre. Ceci a de nombreuses 121 | applications, depuis les scripts de build 122 | jusqu'aux interfaces utilisateur. 123 | 124 | La classe suivante définit le "schema" 125 | d'un mini-langage pour définir des 126 | tables. 127 | 128 | */ 129 | 130 | String center(String content, Integer size) { 131 | value padding = size-content.size; 132 | value paddingBefore = padding/2; 133 | value paddingAfter = padding-paddingBefore; 134 | return " ".repeat(paddingBefore) + content + 135 | " ".repeat(paddingAfter); 136 | } 137 | 138 | class Cell({String*} content) { 139 | shared actual String string { 140 | value result = StringBuilder(); 141 | for (s in content) { 142 | result.append(s); 143 | } 144 | return result.string; 145 | } 146 | } 147 | 148 | class Row({Cell*} cell) { 149 | shared Cell[] cells = cell.sequence(); 150 | shared actual String string { 151 | value result = StringBuilder(); 152 | result.append("|"); 153 | for (cell in cells) { 154 | result.append(center(cell.string, 45)); 155 | result.append("|"); 156 | } 157 | return result.string; 158 | } 159 | } 160 | 161 | class Table(String title, Row header, {Row*} rows) { 162 | shared actual String string { 163 | value result = StringBuilder(); 164 | value size = header.cells.size*46+1; 165 | result.append(center(title, size) + "\n"); 166 | result.append(center("-".repeat(title.size), size) + "\n"); 167 | result.append(header.string.replace("|", " ")+"\n"); 168 | result.append("-".repeat(size) + "\n"); 169 | for (row in rows) { 170 | result.append(row.string+"\n"); 171 | result.append("-".repeat(row.cells.size*46+1) + "\n"); 172 | } 173 | return result.string; 174 | } 175 | } 176 | 177 | /* 178 | 179 | Maintenant nous pouvons définir une table 180 | en utilisant une syntaxe particulièrement 181 | intuitive, ou le code représente la 182 | structure de la table elle-même : 183 | 184 | */ 185 | 186 | Table table = Table { 187 | title = "Ceylon Project"; 188 | header = Row { 189 | Cell { "Module" }, 190 | Cell { "Description" }, 191 | Cell { "URL" } 192 | }; 193 | Row { 194 | Cell { "ceylon-spec" }, 195 | Cell { "The specification and typechecker" }, 196 | Cell { "https://github.com/ceylon/ceylon-spec" } 197 | }, 198 | Row { 199 | Cell { "ceylon-compiler" }, 200 | Cell { "The backend for the JVM" }, 201 | Cell { "https://github.com/ceylon/ceylon-compiler" } 202 | }, 203 | Row { 204 | Cell { "ceylon-js" }, 205 | Cell { "The backend for JavaScript" }, 206 | Cell { "https://github.com/ceylon/ceylon-js" } 207 | }, 208 | Row { 209 | Cell { "ceylon.language" }, 210 | Cell { "The language module" }, 211 | Cell { "https://github.com/ceylon/ceylon.language" } 212 | } 213 | }; 214 | 215 | shared void printTable() { 216 | print(table); 217 | } -------------------------------------------------------------------------------- /source/en/02classes.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Every value is an instance of a class. The 4 | simplest classes just package together some 5 | related state into attributes. 6 | 7 | Classes have members: 8 | 9 | - parameters, 10 | - methods (member functions), 11 | - attributes (member values), and 12 | - member classes. 13 | 14 | Any member annotated shared forms part of the 15 | API of the class and may be accessed from 16 | outside the class. 17 | 18 | For simple classes, we usually need to refine 19 | members string, equals(), and hash that are 20 | inherited from the default supertype Basic. 21 | 22 | A member annotated actual refines a member of 23 | a supertype of the class. 24 | 25 | */ 26 | 27 | class Time(shared Integer hour, 28 | shared Integer minute) { 29 | 30 | shared Integer secondsSinceMidnight => 31 | minute%60*60 + hour*60*60; 32 | 33 | shared actual Integer hash => 34 | secondsSinceMidnight; 35 | 36 | shared actual Boolean equals(Object that) { 37 | //the "is Time" construct tests and 38 | //narrows the type of a value 39 | if (is Time that) { 40 | //that is of type Time here 41 | return secondsSinceMidnight == 42 | that.secondsSinceMidnight; 43 | } 44 | else { 45 | return false; 46 | } 47 | } 48 | 49 | //if writing "shared actual Type" gets too 50 | //boring, you can use this shortcut syntax! 51 | string => "``hour``:``minute``"; 52 | 53 | } 54 | 55 | shared void tryOutTime() { 56 | Time time1 = Time(13,30); 57 | print(time1); 58 | Time time2 = Time(37,30); 59 | print(time2); 60 | Time time3 = Time(13,29); 61 | print(time2); 62 | print(time1==time2); 63 | print(time1==time3); 64 | print(time1.secondsSinceMidnight); 65 | } 66 | 67 | /* 68 | 69 | For testing classes, assertions are very 70 | useful. Run this function to see an assertion 71 | failure occur. 72 | 73 | */ 74 | 75 | //TODO: get the assertions passing 76 | shared void testTime() { 77 | Time time1 = Time(13,30); 78 | assert (time1.string=="13:30"); 79 | Time time2 = Time(37,30); 80 | assert (time2.string=="37:30"); 81 | Time time3 = Time(13,29); 82 | assert (time3.string=="13:29"); 83 | assert (time1==time2); 84 | assert (time1!=time3); 85 | assert (time1.secondsSinceMidnight==48600); 86 | } 87 | 88 | /* 89 | 90 | EXERCISE 91 | 92 | Fix the Time class so that all the assertions 93 | pass. 94 | 95 | */ 96 | 97 | /* 98 | 99 | Some classes have mutable state. If an attribute 100 | is mutable, it must be annotated variable. 101 | 102 | */ 103 | 104 | class Counter(count=0) { 105 | shared variable Integer count; 106 | shared void inc() => count++; 107 | string => count.string; 108 | } 109 | 110 | shared void testCounter() { 111 | value counter = Counter(); 112 | assert (counter.count==0); 113 | counter.inc(); 114 | counter.inc(); 115 | assert (counter.count==2); 116 | counter.count = 0; 117 | assert (counter.count==0); 118 | } 119 | 120 | /* 121 | 122 | EXERCISE 123 | 124 | Add a reset() method to Counter. 125 | 126 | If you're clever, you can get the IDE to 127 | write almost all the code for you. First 128 | uncomment the test code below, and notice 129 | the error span. Hover over the error span 130 | to see the error message. 131 | 132 | Now, position the caret inside the error 133 | span, and let the IDE propose a partial 134 | fix by: 135 | 136 | - In IntelliJ: 137 | 138 | clicking the red lightbulb icon 139 | 140 | - In Eclipse: 141 | 142 | selecting Source > Quick Fix/Assist 143 | 144 | */ 145 | 146 | //TODO: uncomment this test and get it to pass 147 | //shared void testReset() { 148 | // value counter = Counter(); 149 | // assert (counter.count==0); 150 | // counter.inc(); 151 | // counter.inc(); 152 | // assert (counter.count==2); 153 | // counter.reset(); 154 | // assert (counter.count==0); 155 | //} 156 | 157 | /* 158 | 159 | A subclass may extend our class, and refine 160 | its members. A class member may be refined if 161 | it is annotated default. 162 | 163 | */ 164 | 165 | class SecondTime(Integer hour, 166 | Integer minute, 167 | second) 168 | extends Time(hour, minute) { 169 | 170 | //We can declare the type and annotations 171 | //of a parameter in the body of the class 172 | //to clean up the parameter list. 173 | shared Integer second; 174 | 175 | /* 176 | 177 | Ooops! The following code contains an error! 178 | 179 | First uncomment the code, and then fix the 180 | error by adding a 'default' annotation to 181 | Time.secondsSinceMidnight, above. 182 | 183 | */ 184 | 185 | //TODO: uncomment and fix the error! 186 | //secondsSinceMidnight => 187 | // super.secondsSinceMidnight+second%60; 188 | 189 | } 190 | 191 | /* 192 | 193 | EXERCISE 194 | 195 | Fix the implementation of Time and SecondTime 196 | so that the following tests pass. 197 | 198 | */ 199 | 200 | //TODO: get the assertions passing 201 | shared void testSecondTime() { 202 | Time time = Time(13,30); 203 | assert (time.string=="13:30"); 204 | SecondTime stime1 = SecondTime(13,30,00); 205 | SecondTime stime2 = SecondTime(13,30,25); 206 | assert (time==stime1); 207 | assert (time!=stime2); 208 | assert (stime1.string=="13:30:00"); 209 | assert (stime2.string=="13:30:25"); 210 | } 211 | 212 | /* 213 | 214 | An anonymous class declaration defines an 215 | instance. It's a combination value and 216 | class declaration. 217 | 218 | */ 219 | 220 | object midnight extends SecondTime(0,0,0) { 221 | //TODO: uncomment and fix the error 222 | //string => "midnight"; 223 | } 224 | 225 | /* 226 | 227 | An abstract class is a class which can't be 228 | instantiated. It may declare formal members, 229 | which must be implemented by concrete 230 | subclasses of the abstract class. 231 | 232 | An enumerated type is an abstract class or 233 | interface which enumerates (restricts) its 234 | subtypes. 235 | 236 | */ 237 | 238 | abstract class LinkedList() 239 | of Cons | empty { 240 | shared formal Integer length; 241 | } 242 | 243 | //this case of the enumerated type is a class 244 | class Cons(shared T first, 245 | shared LinkedList rest) 246 | extends LinkedList() { 247 | length=>rest.length+1; 248 | } 249 | 250 | //this case of the enumerated type is a 251 | //singleton object 252 | object empty 253 | extends LinkedList() { 254 | length=>0; 255 | } 256 | 257 | String formatLinkedList(LinkedList list) { 258 | //We use a switch statement to handle 259 | //the cases of the enumerated type. The 260 | //compiler validates that the switch 261 | //exhausts all cases 262 | switch (list) 263 | case (empty) { 264 | return ""; 265 | } 266 | case (is Cons) { 267 | value rest = list.rest; 268 | value firstString = list.first.string; 269 | switch (rest) 270 | case (empty) { 271 | return firstString; 272 | } 273 | else { 274 | return firstString + ", " + 275 | formatLinkedList(rest); 276 | } 277 | } 278 | } 279 | 280 | shared void testLinkedList() { 281 | value list = Cons("Smalltalk", Cons("Java", Cons("Ceylon", empty))); 282 | assert (list.length==3); 283 | print(formatLinkedList(list)); 284 | } 285 | 286 | /* 287 | 288 | EXERCISE 289 | 290 | Write a Java-style enum in Ceylon. The classic 291 | example is the Suit enum, with cases hearts, 292 | diamonds, clubs, spades. 293 | 294 | */ 295 | 296 | //TODO: write a Suit class here 297 | -------------------------------------------------------------------------------- /source/es/01bases.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Un programa es simplemente una función de 4 | primer nivel sin parámetros. Para correr el 5 | programa, selecciona el nombre de la función y 6 | ve a: 7 | 8 | Run > Run As > Ceylon Application 9 | 10 | */ 11 | 12 | shared void hello() { 13 | print("¡Hola, Mundo!"); 14 | } 15 | 16 | /* 17 | 18 | EJERCICIO 19 | 20 | ¡Probablemente quieras saber lo que hace 21 | print()! Puedes: 22 | - poner el cursor sobre el nombre de la 23 | función para ver su documentación, o 24 | - mantener pulsada la tecla ctrl o command 25 | mientras haces click sobre el nombre de la 26 | función para navegar a su declaración. 27 | 28 | */ 29 | 30 | /* 31 | 32 | hola() y print() son ejemplos de funciones de 33 | primer nivel - no necesitamos una instancia 34 | de un objeto para invocarlas. 35 | 36 | Una función de primer nivel puede aceptar 37 | argumentos y devolver un valor, en cuyo caso 38 | debemos especificar los tipos de los parámetros, 39 | así como el tipo de retorno. 40 | 41 | Un parámetro puede tener un valor por defecto. 42 | 43 | */ 44 | 45 | String greeting(String name = "Mundo") { 46 | //Las expresiones interpoladas se rodean 47 | //con doble "backtick" dentro de una cadena. 48 | return "Hola, ``name``!"; 49 | } 50 | 51 | /* 52 | 53 | Cuando una función sólo devuelve una expresión, 54 | se puede abreviar usando la fecha gorda. 55 | 56 | */ 57 | 58 | shared void helloName() => print(greeting("Ceylon")); 59 | 60 | shared void helloWorld() => print(greeting()); 61 | 62 | /* 63 | 64 | Un parámetro puede ser variádico, es decir, 65 | puede aceptar múltiples valores. 66 | 67 | */ 68 | 69 | Integer sum(Integer* numbers) { 70 | variable value sum = 0; //Los valores asignables deben anotarse con "variable" 71 | for (x in numbers) { 72 | sum+=x; 73 | } 74 | return sum; 75 | } 76 | 77 | shared void calculateSums() { 78 | 79 | //Una suma sin números 80 | print(sum()); 81 | 82 | //La suma de dos números 83 | print(sum(2, 2)); 84 | 85 | //La suma de los números del 0 al 10 86 | //inclusive, usando el operador de rango .. 87 | //y el operador desplegar * 88 | print(sum(*(0..10))); 89 | 90 | //Y para algo más interesante, la suma 91 | //de todos los números cuadrados del 0 al 100. 92 | //El operator ^ es para exponenciar. 93 | print(sum(for (n in 0..10) n^2)); 94 | 95 | } 96 | 97 | /* 98 | 99 | Las variables se llaman "valores" en Ceylon, 100 | ¡porque realmente no son variables por defecto! 101 | 102 | */ 103 | 104 | shared void greet() { 105 | String greeting = "hei verden"; 106 | //TODO: ¡usa el IDE para rellenar el resto! 107 | } 108 | 109 | /* 110 | 111 | EJERCICIO 112 | 113 | Rellena el resto de esta función. No, no 114 | queremos que escribas a mano una llamada 115 | trivial a print(). Queremos que dejes al IDE 116 | hacerlo por ti: 117 | 118 | - Teclea parte del nombre de la función a la que 119 | quieres llamar. 120 | 121 | - ctrl-espacio activa el autocompletado. 122 | 123 | - Seleccionar una función te lleva al modo 124 | enlazado, donde puedes rellenar rápidamente 125 | los argumentos. Utiliza el tabulador para 126 | navegar entre ellos. 127 | 128 | - esc o un caracter tecleado fuera de los 129 | campos del modo enlazado abandona el modo 130 | enlazado. 131 | 132 | */ 133 | 134 | 135 | /* 136 | 137 | Un valor puede ser una constante (dentro 138 | de cierto alcance), una variable, o un 139 | accesor de lectura (getter) 140 | 141 | */ 142 | 143 | //Una constante de primer nivel 144 | Integer zero = 0; 145 | 146 | //Una variable con valor inicial 147 | variable Integer int = zero; 148 | 149 | //Un getter definido con flecha gorda 150 | Integer intSquared => int^2; 151 | 152 | //Un getter con todo un bloque de código 153 | Integer intFactorial { 154 | variable value fac = 1; 155 | for (i in 1..int) { 156 | fac*=i; 157 | } 158 | return fac; 159 | } 160 | 161 | shared void values() { 162 | int = 3; 163 | print("i = ``int``"); 164 | print("i^2 = ``intSquared``"); 165 | print("i! = ``intFactorial``"); 166 | int = 4; 167 | print("i = ``int``"); 168 | print("i^2 = ``intSquared``"); 169 | print("i! = ``intFactorial``"); 170 | } 171 | 172 | /* 173 | 174 | El compilador puede inferir el tipo de una 175 | declaración local. Pon el cursor sobre la 176 | palabra reservada "value" o "function" para 177 | ver el tipo inferido de la declaración. 178 | 179 | El tipo de un parámetro de una función no 180 | puede ser inferido. 181 | 182 | */ 183 | 184 | shared void inferredTypes() { 185 | value time = system.milliseconds; 186 | value nl = operatingSystem.newline; 187 | function sqr(Float float) => float*float; 188 | } 189 | 190 | /* 191 | 192 | EJERCICIO 193 | 194 | Pon el cursor sobre una palabra reservada 195 | "value" o "function" y selecciona en el menú: 196 | 197 | Source > Quick Fix / Assist 198 | 199 | Y luego selecciona 'Specify type' del menú 200 | emergente. 201 | 202 | O, selecciona la función completa, y la opción: 203 | 204 | Source > Reveal Inferred Types 205 | 206 | */ 207 | 208 | /* 209 | 210 | Se pueden escapar caracteres Unicode. 211 | Por ejemplo "pi" puede escribirse \{#03C0}. 212 | 213 | ups... la consola de Eclipse está idiota. 214 | Ve a: 215 | 216 | Project > Properties > Resource 217 | 218 | y configura la codificación de tus archivos 219 | de texto a UTF-8 para correr este programa 220 | y que salga bien el resultado. 221 | 222 | */ 223 | 224 | shared void helloPi() => print(greeting("\{#03C0}")); 225 | 226 | /* 227 | 228 | O, mucho más verboso, pero también mucho 229 | más entendible, podemos usar el nombre del 230 | carácter Unicode. 231 | 232 | */ 233 | 234 | shared void helloPi2() 235 | => print(greeting("\{GREEK SMALL LETTER PI}")); 236 | 237 | /* 238 | 239 | Pero, ¿y si queremos imprimir "\{#03C0}"? 240 | Podemos usar un escape de diagonal inversa, 241 | o un string verbatim. 242 | 243 | */ 244 | 245 | shared void printTheUnicodeEscapeForPi() { 246 | 247 | //La doble diagonal inversa se convierte en su literal 248 | print("\\{#03C0}"); 249 | 250 | //Las comillas triples delimitan una cadena 251 | //verbatim donde no se interpretan secuencias 252 | //de escape. 253 | print("""\{#03C0}"""); 254 | 255 | } 256 | 257 | /* 258 | 259 | Hay cadenas que pueden abarcar varias líneas, 260 | especialmente cuando documentamos nuestro 261 | código en formato markdown. Deja el puntero 262 | del mouse sobre el nombre de esta función 263 | para ver su documentación. 264 | 265 | */ 266 | 267 | "Este programa imprime: 268 | 269 | - El _nombre de la máquina virtual,_ 270 | - La _versión de la máquina virtual,_ y 271 | - La _versión de Ceylon._ 272 | 273 | Utiliza los objetos [[operatingSystem]] y [[language]] 274 | definidos en `ceylon.language`, el módulo 275 | de lenguaje de Ceylon." 276 | shared void printInfo() => 277 | print("Máquina virtual: ``operatingSystem.name`` 278 | versión: ``operatingSystem.version`` 279 | lenguaje: ``language.version`` (``language.versionName``)"); 280 | //tip: Intenta usar el atributo "normalized" de la cadena. 281 | 282 | /* 283 | 284 | Las anotaciones especifican metadatos de un elemento de programa. 285 | 286 | */ 287 | 288 | by ("Gavin") 289 | throws (`class Exception`) 290 | deprecated ("Bien, este programa no es muy útil. 291 | Prueba [[hello]] mejor.") 292 | see (`function hello`) 293 | shared void failNoisily() { 294 | throw Exception("¡aaaaarrrrrggggghhhhhhh!"); 295 | } 296 | 297 | /* 298 | 299 | EJERCICIO 300 | 301 | Escribe un programa que imprima todos los 302 | números primos entre 1 y 100. ¡No olvides documentar 303 | tu trabajo! 304 | 305 | */ 306 | 307 | //TODO: imprimir los números primos 308 | -------------------------------------------------------------------------------- /source/es/02clases.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Un valor es una instancia de una clase. Las 4 | clases más simples lo único que hacen es 5 | juntar cierto estado relacionado en atributos. 6 | 7 | Las clases tienen miembros: 8 | 9 | - parámetros, 10 | - métodos (funciones miembro), 11 | - atributos (valores miembro), y 12 | - clases miembro. 13 | 14 | Cualquier miembro anotado como "shared" forma 15 | parte del API de la clase y se puede acceder 16 | a él desde fuera de la misma. 17 | 18 | Para clases simples, por lo general hay que 19 | refinar los miembros string, equals() y hash, 20 | que son heredados de Basic, el supertipo por 21 | defecto. 22 | 23 | Un miembro anotado como "actual" refina un 24 | miembro de un supertipo de la clase. 25 | 26 | */ 27 | 28 | class Time(shared Integer hour, 29 | shared Integer minute) { 30 | 31 | shared Integer secondsSinceMidnight => 32 | minute%60*60 + hour*60*60; 33 | 34 | shared actual Integer hash => 35 | secondsSinceMidnight; 36 | 37 | shared actual Boolean equals(Object that) { 38 | //"is Time" prueba y acota el tipo 39 | //del valor. 40 | if (is Time that) { 41 | //"that" es de tipo Time en este bloque 42 | return secondsSinceMidnight == 43 | that.secondsSinceMidnight; 44 | } 45 | else { 46 | return false; 47 | } 48 | } 49 | 50 | //si te aburre escribir "shared actual Type", 51 | //puedes usar esta abreviación 52 | string => "``hour``:``minute``"; 53 | 54 | } 55 | 56 | shared void tryOutTime() { 57 | Time time1 = Time(13,30); 58 | print(time1); 59 | Time time2 = Time(37,30); 60 | print(time2); 61 | Time time3 = Time(13,29); 62 | print(time2); 63 | print(time1==time2); 64 | print(time1==time3); 65 | print(time1.secondsSinceMidnight); 66 | } 67 | 68 | /* 69 | 70 | Las aserciones son muy útiles para probar 71 | clases. Corre esta función para ver cómo 72 | ocurre una falla de aserción. 73 | 74 | */ 75 | 76 | //TODO: hacer que las aserciones pasen 77 | shared void testTime() { 78 | Time time1 = Time(13,30); 79 | assert (time1.string=="13:30"); 80 | Time time2 = Time(37,30); 81 | assert (time2.string=="37:30"); 82 | Time time3 = Time(13,29); 83 | assert (time3.string=="13:29"); 84 | assert (time1==time2); 85 | assert (time1!=time3); 86 | assert (time1.secondsSinceMidnight==48600); 87 | } 88 | 89 | /* 90 | 91 | EJERCICIO 92 | 93 | Modificar la clase Time para que las aserciones 94 | pasen. 95 | 96 | */ 97 | 98 | /* 99 | 100 | Algunas clases tienen estado mutable. Si un 101 | atributo es mutable, debe ser anotado "variable". 102 | 103 | */ 104 | 105 | class Counter(count=0) { 106 | shared variable Integer count; 107 | shared void inc() => count++; 108 | string => count.string; 109 | } 110 | 111 | shared void testCounter() { 112 | value counter = Counter(); 113 | assert (counter.count==0); 114 | counter.inc(); 115 | counter.inc(); 116 | assert (counter.count==2); 117 | counter.count = 0; 118 | assert (counter.count==0); 119 | } 120 | 121 | /* 122 | 123 | EJERCICIO 124 | 125 | Agregar un método reset() a la clase Counter. 126 | 127 | Si eres ingenioso, puedes lograr que el IDE 128 | escriba casi todo el código por ti. Primero 129 | descomenta el código de prueba aquí abajo, 130 | y observa el error que te indica el IDE. 131 | Pon el mouse sobre el error, o utiliza: 132 | 133 | Edit > Quick Fix / Assist 134 | 135 | con el cursor sobre el error, para que el IDE 136 | te proponga una solución parcial. 137 | 138 | */ 139 | 140 | //TODO: descomentar esta prueba y hacer que pase 141 | //shared void testReset() { 142 | // value counter = Counter(); 143 | // assert (counter.count==0); 144 | // counter.inc(); 145 | // counter.inc(); 146 | // assert (counter.count==2); 147 | // counter.reset(); 148 | // assert (counter.count==0); 149 | //} 150 | 151 | /* 152 | 153 | Una subclase puede extender nuestra clase, y 154 | refinar sus miembros. Un miembro de clase puede 155 | ser refinado sólo si está anotado como "default". 156 | 157 | */ 158 | 159 | class SecondTime(Integer hour, 160 | Integer minute, 161 | second) 162 | extends Time(hour, minute) { 163 | 164 | //Podemos declarar el tipo y las 165 | //anotaciones de un parámetro en el cuerpo 166 | //de la clase, para dejar más limpia la lista 167 | //de parámetros. 168 | shared Integer second; 169 | 170 | /* 171 | 172 | Ups, el siguiente código tiene un error... 173 | 174 | Primero descomenta el código, y luego 175 | arréglalo agregando una anotación "default" 176 | a Time.secondsSinceMidnight arriba. 177 | 178 | */ 179 | 180 | //TODO: descomentar y arreglar el error 181 | //secondsSinceMidnight => 182 | // super.secondsSinceMidnight+second%60; 183 | 184 | } 185 | 186 | /* 187 | 188 | EJERCICIO 189 | 190 | Arreglar la implementación de Time y SecondTime 191 | para que las siguientes pruebas pasen. 192 | 193 | */ 194 | 195 | //TODO: hacer que estas aserciones pasen. 196 | shared void testSecondTime() { 197 | Time time = Time(13,30); 198 | assert (time.string=="13:30"); 199 | SecondTime stime1 = SecondTime(13,30,00); 200 | SecondTime stime2 = SecondTime(13,30,25); 201 | assert (time==stime1); 202 | assert (time!=stime2); 203 | assert (stime1.string=="13:30:00"); 204 | assert (stime2.string=="13:30:25"); 205 | } 206 | 207 | /* 208 | 209 | Una declaración de clase anónima define una 210 | instancia. Es una declaración combinada de 211 | valor y clase. 212 | 213 | */ 214 | 215 | object midnight extends SecondTime(0,0,0) { 216 | //TODO: descomentar y arreglar el error 217 | //string => "medianoche"; 218 | } 219 | 220 | /* 221 | 222 | Una clase abstracta es una clase que no se 223 | puede instanciar. Puede declarar miembros 224 | "formal", que deben ser implementados por 225 | subclases concretas de la clase abstracta. 226 | 227 | Un tipo enumerado es una clase o interfaz 228 | abstracta que enumera (restringe) sus 229 | subtipos. 230 | 231 | */ 232 | 233 | abstract class LinkedList() 234 | of Cons | empty { 235 | shared formal Integer length; 236 | } 237 | 238 | //Este caso del tipo enumerado es 239 | //una clase 240 | class Cons(shared T first, 241 | shared LinkedList rest) 242 | extends LinkedList() { 243 | length=>rest.length+1; 244 | } 245 | 246 | //Ese caso del tipo enumerado es un 247 | //objeto singleton 248 | object empty 249 | extends LinkedList() { 250 | length=>0; 251 | } 252 | 253 | String formatLinkedList(LinkedList list) { 254 | //Usamos el switch para manejar los casos 255 | //de un tipo enumerador. El compilador valida 256 | //que el switch considere todos los casos. 257 | switch (list) 258 | case (empty) { 259 | return ""; 260 | } 261 | case (is Cons) { 262 | value rest = list.rest; 263 | value firstString = list.first.string; 264 | switch (rest) 265 | case (empty) { 266 | return firstString; 267 | } 268 | else { 269 | return firstString + ", " + 270 | formatLinkedList(rest); 271 | } 272 | } 273 | } 274 | 275 | shared void testLinkedList() { 276 | value list = Cons("Smalltalk", Cons("Java", Cons("Ceylon", empty))); 277 | assert (list.length==3); 278 | print(formatLinkedList(list)); 279 | } 280 | 281 | /* 282 | 283 | EJERCICIO 284 | 285 | Escribe un enum estilo Java pero en Ceylon. 286 | El ejemplo clásico es el de la baraja, con 287 | los casos corazones, diamantes, picas y tréboles. 288 | 289 | */ 290 | 291 | //TODO: escribir una clase Baraja 292 | -------------------------------------------------------------------------------- /source/en/05iterables_sequences.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | We've already met the type Iterable. We 4 | usually write: 5 | 6 | - {T*} to mean an iterable of zero or more 7 | instances of T 8 | - {T+} to mean an iterable of one or more 9 | instances of T 10 | 11 | In fact, these type abbreviations just mean 12 | Iterable and Iterable, 13 | respectively. 14 | 15 | Of course, {T+} is a subtype of {T*}. 16 | 17 | We can construct an iterable using braces. 18 | 19 | */ 20 | 21 | {String*} noStrings = {}; 22 | {String+} twoStrings = {"hello", "world"}; 23 | {String+} manyStrings = { for (n in 0..100) n.string }; 24 | 25 | /* 26 | 27 | Iterable defines a large suite of methods for 28 | working with streams of values. For example, 29 | Iterable has the famous methods map(), 30 | filter(), and fold(). 31 | 32 | Here we're letting Ceylon infer the type of 33 | the anonymous function parameters. 34 | 35 | */ 36 | 37 | shared void demoMapFilterFold() { 38 | print((1..100) 39 | .filter((i) => i%3==0) 40 | .map((i) => i^2) 41 | //TODO: replace fold() with String.join() 42 | .fold("")((partial, ii) 43 | => partial + ", " + ii.string)); 44 | } 45 | 46 | /* 47 | 48 | EXERCISE 49 | 50 | Clean up the code above using String.join(). 51 | 52 | */ 53 | 54 | /* 55 | 56 | The code above is quite noisy. It's much more 57 | usual to use comprehensions to express this 58 | kind of thing. 59 | 60 | */ 61 | 62 | shared void demoComprehension() { 63 | value squares = { 64 | for (i in 1..100) 65 | if (i%3==0) 66 | (i^2).string 67 | }; 68 | print(", ".join(squares)); 69 | } 70 | 71 | /* 72 | 73 | A comprehension may have multiple for and if 74 | clauses. 75 | 76 | */ 77 | 78 | {Character*} ss = { 79 | for (arg in process.arguments) 80 | for (c in arg) 81 | if (c.uppercase) 82 | c.uppercased 83 | }; 84 | 85 | /* 86 | 87 | So we often have a choice of two different 88 | ways to express something: 89 | 90 | - using anonymous functions, or 91 | - using a comprehension. 92 | 93 | */ 94 | 95 | Boolean allNumbers1 96 | = manyStrings.every((s) 97 | => !Integer.parse(s) is ParseException); 98 | 99 | Boolean allNumbers2 100 | = every { 101 | for (s in manyStrings) 102 | !Integer.parse(s) is ParseException 103 | }; 104 | 105 | /* 106 | 107 | A sequence is an immutable list of values 108 | of finite length. Sequence types are written 109 | [T*] or [T+]. For reasons of tradition, we 110 | are also allowed to write [T*] as T[]. 111 | 112 | In fact, these are just abbreviations for 113 | Sequential and Sequence. 114 | 115 | We can construct a sequence using brackets. 116 | 117 | */ 118 | 119 | [String*] noStringsSeq = []; 120 | [String+] twoStringsSeq = ["hello", "world"]; 121 | [String+] manyStringsSeq = [ for (n in 0..100) n.string ]; 122 | 123 | /* 124 | 125 | The empty sequence [] is of type Empty, which 126 | we write []. 127 | 128 | */ 129 | 130 | [] emptySeq = []; 131 | 132 | /* 133 | 134 | We can access the elements of a sequence (or 135 | of any other kind of List) using the index 136 | operator. 137 | 138 | */ 139 | 140 | shared void testSequenceIndexing() { 141 | 142 | //the single-index indexing operator 143 | //results in a possibly-null type! 144 | //(there is no IndexOutOfBoundsException) 145 | assert(exists world = twoStringsSeq[1], 146 | world=="world"); 147 | 148 | //the closed and open ranged indexing 149 | //operators result in a sequence 150 | assert(manyStringsSeq[1..2]==["1", "2"]); 151 | assert(manyStringsSeq[99...]==["99", "100"]); 152 | 153 | } 154 | 155 | /* 156 | We can narrow a possibly-empty sequence (a 157 | [T*]) to nonempty sequence (a [T+]) using the 158 | nonempty operator. 159 | */ 160 | 161 | shared void demoNonempty() { 162 | if (nonempty args = process.arguments) { 163 | //hover over args and first to see 164 | //their types! 165 | value first = args.first; 166 | print(first); 167 | } 168 | } 169 | 170 | /* 171 | 172 | We can iterate the indexes and elements of 173 | a sequence (or any other kind of List). 174 | 175 | */ 176 | 177 | shared void demoForWithIndexes() { 178 | for (i->s in twoStringsSeq.indexed) { 179 | print("``i`` -> ``s``"); 180 | } 181 | } 182 | 183 | /* 184 | 185 | Tuples are a special kind of sequence: a 186 | typed linked list. Tuple types are specified 187 | by listing element types in brackets, and a 188 | tuple is created by listing its elements 189 | in brackets. 190 | 191 | */ 192 | 193 | [Float, Integer, String, String] tuple 194 | = [0.0, 22, "hello", "world"]; 195 | 196 | /* 197 | 198 | Elements may be retrieved from a tuple 199 | without losing any typing information. 200 | 201 | */ 202 | 203 | shared void demoTupleIndexing() { 204 | Null nil1 = tuple[-1]; 205 | Float float = tuple[0]; 206 | Integer int = tuple[1]; 207 | String string1 = tuple[2]; 208 | String string2 = tuple[3]; 209 | Null nil2 = tuple[4]; 210 | } 211 | 212 | /* 213 | 214 | Really, all this is just syntax sugar for 215 | the Tuple class. We always use the sugar 216 | in this case; we never want to write the 217 | following: 218 | 219 | */ 220 | 221 | shared void desugaredTuple() { 222 | Tuple> pair 223 | = Tuple(1.0,Tuple("hello",[])); 224 | Float float = pair.first; 225 | String string = pair.rest.first; 226 | Null nil = pair.rest.rest.first; 227 | } 228 | 229 | /* 230 | 231 | EXERCISE 232 | 233 | Go check out the declaration of Tuple to 234 | understand how this works! 235 | 236 | */ 237 | 238 | /* 239 | 240 | We can use the spread operator to pass a tuple 241 | containing arguments to a function. Remember that 242 | a function type consists of a return type and a 243 | tuple type encoding the parameter types? Well, 244 | the spread argument tuple must be assignable to 245 | that tuple type. 246 | 247 | */ 248 | 249 | shared void demoSpreadTuple() { 250 | value args = [(Character c) => !c.letter, true]; 251 | for (word in "Hello, World! Goodbye.".split(*args)) { 252 | print(word); 253 | } 254 | } 255 | 256 | /* 257 | 258 | We can use tuples to define functions with multiple 259 | return values. 260 | 261 | */ 262 | 263 | //a function that produces a tuple 264 | [String, String?, String] parseName(String name) { 265 | value it = name.split().iterator(); 266 | "first name is required" 267 | assert (is String first = it.next()); 268 | "last name is required" 269 | assert (is String second = it.next()); 270 | if (is String third = it.next()) { 271 | return [first, second, third]; 272 | } 273 | else { 274 | return [first, null, second]; 275 | } 276 | } 277 | 278 | /* 279 | 280 | The spread operator and the unflatten() function 281 | help us compose such functions. 282 | 283 | */ 284 | 285 | //a function with multiple parameters 286 | String welcome(String first, String? middle, String last) => 287 | "Welcome, ``first`` ``last``!"; 288 | 289 | shared void demoFunctionComposition() { 290 | //the * operator "spreads" the tuple result 291 | //of parseName() over the parameters of 292 | //welcome() 293 | print(welcome(*parseName("John Doe"))); 294 | 295 | //but what if we want to compose parseName() 296 | //and welcome() without providing arguments 297 | //up front? Well, we can use compose() and 298 | //unflatten() 299 | value greet = compose(print, 300 | compose(unflatten(welcome), parseName)); 301 | greet("Jane Doe"); 302 | 303 | //so we could actually re-express the first 304 | //example in terms of unflatten() 305 | print(unflatten(welcome)(parseName("Jean Doe"))); 306 | } -------------------------------------------------------------------------------- /source/en/03null_unions_intersections.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet 3 | } 4 | /* 5 | 6 | A union type represents a choice between 7 | types. A union type is written A|B for any 8 | types A and B. It's pronounced "A or B". 9 | 10 | */ 11 | 12 | void printDouble(String|Integer|Float val) { 13 | String|Integer|Float double; 14 | switch (val) 15 | case (is String) { double = val+val; } 16 | case (is Integer) { double = 2*val; } 17 | case (is Float) { double = 2.0*val; } 18 | print("double ``val`` is ``double``"); 19 | } 20 | 21 | shared void testDouble() { 22 | printDouble("hello"); 23 | printDouble(111); 24 | printDouble(0.111); 25 | } 26 | 27 | /* 28 | 29 | We deal with missing or "null" values using 30 | union types. The class Null has a single 31 | instance named null, which represents a 32 | missing value. Therefore, a possibly-null 33 | string is represented by the type Null|String. 34 | 35 | We can write the type "Null|String" using the 36 | special syntactic abbreviation "String?". 37 | This is just syntax sugar for the union type! 38 | 39 | The type "String?" is pronounced "maybe String". 40 | 41 | Run the following program with and without a 42 | command line argument. (You can set a command 43 | line argument using: 44 | 45 | Run > Run Configurations... 46 | 47 | */ 48 | 49 | shared void helloArguments() { 50 | String? name = process.arguments[0]; 51 | if (is String name) { //TODO: use exists 52 | print("hello " + name); 53 | } 54 | else { 55 | print("hello world"); 56 | } 57 | } 58 | 59 | /* 60 | 61 | EXERCISE 62 | 63 | We usually use the "exists" operator instead 64 | of "is String" in code like this. Change the 65 | program above to use exists. More syntactic 66 | sugar! 67 | 68 | Next, to make the code a little more compact, 69 | change the code to use this form of the if 70 | statement: 71 | 72 | if (is String name = process.arguments[0]) 73 | 74 | (But using "exists", not "is String".) 75 | 76 | */ 77 | 78 | /* 79 | 80 | The 'then' and 'else' operators produce and 81 | consume null values. 82 | 83 | */ 84 | 85 | shared void thenAndElse() { 86 | Integer n = 5; 87 | 88 | print(n>0 then n); 89 | print(n<=0 then n); 90 | 91 | print(n>=0 then "positive" else "strictly negative"); 92 | 93 | print("123456789"[n] else "out of bounds"); 94 | print("12345"[n] else "out of bounds"); 95 | } 96 | 97 | /* 98 | 99 | EXERCISE 100 | 101 | Change the helloArguments() program above to 102 | use an operator instead of if/else. 103 | 104 | */ 105 | 106 | /* 107 | 108 | An intersection represents the combination of 109 | two types. An intersection type is written 110 | A&B for any types A and B. It's pronounced 111 | "A and B". 112 | 113 | Intersection types often arise as a result of 114 | type narrowing. 115 | 116 | Note: the syntax {T*} means an iterable 117 | sequence of values of type T. It's syntactic 118 | sugar for the interface Iterable. 119 | 120 | */ 121 | 122 | T? third({T*} iterable) { 123 | if (is Correspondence iterable) { 124 | //hover over iterable to see its 125 | //narrowed type within this block! 126 | return iterable[2]; 127 | } 128 | else { 129 | value iterator = iterable.iterator(); 130 | iterator.next(); 131 | iterator.next(); 132 | if (is T third = iterator.next()) { 133 | return third; 134 | } 135 | else { 136 | return null; 137 | } 138 | } 139 | } 140 | 141 | shared void testThird() { 142 | assert (exists thrd = third("hello"), 143 | thrd =='l'); 144 | } 145 | 146 | /* 147 | 148 | Cute observation: the type of 'thrd' above is 149 | &Object, which expands to 150 | Null&Object|Character&Object, which simplifies 151 | to Nothing|Character, which further simplifies 152 | to Character. The typechecker does all this 153 | reasoning automatically. 154 | 155 | Union and intersection types are especially 156 | useful when it comes to type inference. 157 | 158 | */ 159 | 160 | shared void demoTypeInference() { 161 | //hover over joined to see its type! 162 | value joined = concatenate("hello", 1..69); 163 | Object[] objects = joined; 164 | print(objects); 165 | } 166 | 167 | /* 168 | 169 | Union and intersection types also help 170 | correctly type the union and intersection 171 | operations on Sets. 172 | 173 | Set union and intersection are defined in 174 | terms of the methods intersection() and 175 | union() of the interface Set. The | and & 176 | operators are syntactic sugar for these 177 | methods. 178 | 179 | Check out the definition of these methods in 180 | Set to see how they are in turn defined in 181 | terms of union/intersection types. 182 | 183 | */ 184 | 185 | shared void demoSets() { 186 | Set chars = HashSet { elements="hello"; }; 187 | Set ints = HashSet { elements=0..10; }; 188 | //hover over intsAndChars to see its type! 189 | value intsAndChars = chars|ints; 190 | print(intsAndChars); 191 | //hover over empty to see its type! 192 | value empty = chars&ints; 193 | print(empty); 194 | } 195 | 196 | /* 197 | 198 | EXERCISE 199 | 200 | The special type Nothing is the "bottom" type, 201 | a type with no instances. 202 | 203 | Explain why the type of the intersection 204 | chars&ints above is Set, given that 205 | the types Character and the type Integer are 206 | disjoint types (have no instances in common). 207 | 208 | P.S. Character and Integer are final classes, 209 | unrelated by inheritance, and therefore 210 | disjoint. 211 | 212 | */ 213 | 214 | /* 215 | 216 | Wait, if Nothing has no values, what is the 217 | meaning of the following well-typed code? 218 | 219 | */ 220 | 221 | shared void thereIsNoNothing() { 222 | Nothing n = nothing; 223 | print(n); 224 | } 225 | 226 | /* 227 | 228 | Well, the type Nothing indicates, in practice, 229 | that a invocation of a function or evaluation 230 | of a value either: 231 | 232 | - does not terminate, or 233 | - throws in an exception. 234 | 235 | It might seem that Nothing isn't very useful 236 | for anything, but it helps a lot when defining 237 | generic types and generic functions. 238 | 239 | */ 240 | 241 | /* 242 | 243 | The useful coalesced attribute also 244 | demonstrates a nice application of 245 | intersection. 246 | 247 | Check out the definition of coalesce() to 248 | see how this works. 249 | 250 | */ 251 | 252 | shared void demoCoalesce() { 253 | 254 | //{String?*} is the type of an iterable of 255 | //strings and nulls 256 | {String?*} stringsAndNulls = {"hello", null, "world"}; 257 | 258 | //{String*} is the type of an iterable of 259 | //just strings (without nulls) 260 | {String*} strings = stringsAndNulls.coalesced; 261 | 262 | assert (strings.sequence() == ["hello", "world"]); 263 | 264 | } 265 | 266 | /* 267 | 268 | There's a special trick we can do with union 269 | types that help us give functions like max() 270 | and min() the correct type. 271 | 272 | The problem is that when we have "zero or 273 | more things", max() can return null. But when 274 | we have "one or more" things, it can't. And 275 | when we have exactly zero things, max() 276 | always returns null. How can we capture this 277 | within the type system? 278 | 279 | */ 280 | 281 | shared void demoMax() { 282 | 283 | Null maxOfZero = max({}); 284 | 285 | {Integer+} oneOrMore = {1, 2}; 286 | Integer maxOfOneOrMore = max(oneOrMore); 287 | 288 | {Integer*} zeroOrMore = {1, 2}; 289 | Integer? maxOfZeroOrMore = max(zeroOrMore); 290 | 291 | } 292 | 293 | /* 294 | 295 | EXERCISE 296 | 297 | Check out the definitions of max() and 298 | Iterable and figure out how this works! 299 | 300 | The solution involves Nothing ;-) 301 | 302 | */ 303 | -------------------------------------------------------------------------------- /source/fr/02classes.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Toutes les valeurs sont des instances d'une 4 | classe. Les classes les plus simples combinent 5 | des états associés en tant qu'attributs. 6 | 7 | Les classes ont des membres : 8 | 9 | - des paramètres, 10 | - des méthodes (fonctions membres), 11 | - des attributs (valeurs membres), et 12 | - des classes membres. 13 | 14 | Tout membre annoté shared fait partie de l'API 15 | de la classe et est accessible depuis 16 | l'extérieur de la classe. 17 | 18 | Pour les classes simples, il est souvent 19 | nécessaire de raffiner les membres string, 20 | equals(), and hash qui sont généralement hérité 21 | du supertype par défaut Basic. 22 | 23 | Un membre annoté actual raffine un membre d'un 24 | supertype de la classe. 25 | 26 | */ 27 | 28 | class Time(shared Integer hour, 29 | shared Integer minute) { 30 | 31 | shared Integer secondsSinceMidnight => 32 | minute%60*60 + hour*60*60; 33 | 34 | shared actual Integer hash => 35 | secondsSinceMidnight; 36 | 37 | shared actual Boolean equals(Object that) { 38 | //La syntaxe "is Time" teste and 39 | //restreint le type de la valeur. 40 | if (is Time that) { 41 | //c'est à dire le type Time ici 42 | return secondsSinceMidnight == 43 | that.secondsSinceMidnight; 44 | } 45 | else { 46 | return false; 47 | } 48 | } 49 | 50 | //si le fait d'écrire "shared actual Type" 51 | //devient lassant, vous pouvez utiliser 52 | //l'abréviation suivante ! 53 | string => "``hour``:``minute``"; 54 | 55 | } 56 | 57 | shared void tryOutTime() { 58 | Time time1 = Time(13,30); 59 | print(time1); 60 | Time time2 = Time(37,30); 61 | print(time2); 62 | Time time3 = Time(13,29); 63 | print(time2); 64 | print(time1==time2); 65 | print(time1==time3); 66 | print(time1.secondsSinceMidnight); 67 | } 68 | 69 | /* 70 | 71 | Pour tester des classes, les assertions sont 72 | très utiles. Exécutez cette fonction afin de 73 | voir une assertion échouer. 74 | 75 | */ 76 | 77 | //TODO: faite en sorte que l'assertion passe. 78 | shared void testTime() { 79 | Time time1 = Time(13,30); 80 | assert (time1.string=="13:30"); 81 | Time time2 = Time(37,30); 82 | assert (time2.string=="37:30"); 83 | Time time3 = Time(13,29); 84 | assert (time3.string=="13:29"); 85 | assert (time1==time2); 86 | assert (time1!=time3); 87 | assert (time1.secondsSinceMidnight==48600); 88 | } 89 | 90 | /* 91 | 92 | EXERCICE 93 | 94 | Corrigez la classe Time afin que toutes les 95 | assertions passent. 96 | 97 | */ 98 | 99 | /* 100 | 101 | Certaines classes ont des états modifiables. Si 102 | un attribut est modifiable, il doit être annoté 103 | variable. 104 | 105 | */ 106 | 107 | class Counter(count=0) { 108 | shared variable Integer count; 109 | shared void inc() => count++; 110 | string => count.string; 111 | } 112 | 113 | shared void testCounter() { 114 | value counter = Counter(); 115 | assert (counter.count==0); 116 | counter.inc(); 117 | counter.inc(); 118 | assert (counter.count==2); 119 | counter.count = 0; 120 | assert (counter.count==0); 121 | } 122 | 123 | /* 124 | 125 | EXERCICE 126 | 127 | Ajoutez une méthode reset() à Counter. 128 | 129 | Si vous êtes malin, vous pouvez utiliser l'IDE 130 | afin qu'il écrive presque tout le code pour vous. 131 | Commencez par décommenter le test ci-dessous, et 132 | remarquez l'indication d'une zone erreur dans le 133 | code. Survolez l'indication de l'erreur, ou 134 | utilisez : 135 | 136 | Edit > Quick Fix / Assist 137 | 138 | tant que votre curseur est dans la zone d'erreur 139 | afin que l'IDE vous propose une résolution 140 | partielle. 141 | 142 | */ 143 | 144 | //TODO : décommentez ce test et faites le passer 145 | //shared void testReset() { 146 | // value counter = Counter(); 147 | // assert (counter.count==0); 148 | // counter.inc(); 149 | // counter.inc(); 150 | // assert (counter.count==2); 151 | // counter.reset(); 152 | // assert (counter.count==0); 153 | //} 154 | 155 | /* 156 | 157 | Une classe fille peut étendre notre classe, et 158 | raffiner ses membres. Un membre d'une classe 159 | peut être raffiné s'il est annoté default. 160 | 161 | */ 162 | 163 | class SecondTime(Integer hour, 164 | Integer minute, 165 | second) 166 | extends Time(hour, minute) { 167 | 168 | //Nous pouvons déclarer le type et les annotations 169 | //d'un paramètre dans le corps de la classe 170 | //afin d'alléger la liste des paramètres. 171 | shared Integer second; 172 | 173 | /* 174 | 175 | Oups ! Le code ci-dessous contient une erreur ! 176 | 177 | Commencez par décommenter le code, puis corriger 178 | l'erreur en ajoutant l'annotation 'default' à 179 | Time.secondsSinceMidnight ci-dessus. 180 | 181 | */ 182 | 183 | //TODO : décommenter et corriger l'erreur ! 184 | //secondsSinceMidnight => 185 | // super.secondsSinceMidnight+second%60; 186 | 187 | } 188 | 189 | /* 190 | 191 | EXERCICE 192 | 193 | Corrigez l'implémentation de Time et SecondTime 194 | afin que le test suivant puisse passer. 195 | 196 | */ 197 | 198 | //TODO : faite passer l'assertion 199 | shared void testSecondTime() { 200 | Time time = Time(13,30); 201 | assert (time.string=="13:30"); 202 | SecondTime stime1 = SecondTime(13,30,00); 203 | SecondTime stime2 = SecondTime(13,30,25); 204 | assert (time==stime1); 205 | assert (time!=stime2); 206 | assert (stime1.string=="13:30:00"); 207 | assert (stime2.string=="13:30:25"); 208 | } 209 | 210 | /* 211 | 212 | Une déclaration de classes anonyme définit 213 | une instance. Elle combine une déclaration 214 | de valeur et de classe. 215 | 216 | */ 217 | 218 | object midnight extends SecondTime(0,0,0) { 219 | //TODO : décommenter et corriger l'erreur. 220 | //string => "midnight"; 221 | } 222 | 223 | /* 224 | 225 | Une classe abstraite est une classe qui ne peut 226 | pas être instanciée. Elle peut déclarer des 227 | membres formels, qui doivent être implémentés 228 | par des sous-classes concrètes de la classe 229 | abstraite. 230 | 231 | Un type énuméré est une classes abstraite ou 232 | une interface qui énumère (restreint) ces sous 233 | types. 234 | 235 | */ 236 | 237 | abstract class LinkedList() 238 | of Cons | empty { 239 | shared formal Integer length; 240 | } 241 | 242 | //Ce cas du type énuméré est une classe 243 | class Cons(shared T first, 244 | shared LinkedList rest) 245 | extends LinkedList() { 246 | length=>rest.length+1; 247 | } 248 | 249 | //ce cas du type énuméré est un objet singleton 250 | object empty 251 | extends LinkedList() { 252 | length=>0; 253 | } 254 | 255 | String formatLinkedList(LinkedList list) { 256 | //Nous utilisons une expression switch pour 257 | //gérer les cas d'un type énuméré. Le 258 | //compilateur valide que le switch 259 | //considère bien l'ensemble des cas. 260 | switch (list) 261 | case (empty) { 262 | return ""; 263 | } 264 | case (is Cons) { 265 | value rest = list.rest; 266 | value firstString = list.first.string; 267 | switch (rest) 268 | case (empty) { 269 | return firstString; 270 | } 271 | else { 272 | return firstString + ", " + 273 | formatLinkedList(rest); 274 | } 275 | } 276 | } 277 | 278 | shared void testLinkedList() { 279 | value list = Cons("Smalltalk", Cons("Java", Cons("Ceylon", empty))); 280 | assert (list.length==3); 281 | print(formatLinkedList(list)); 282 | } 283 | 284 | /* 285 | 286 | EXERCICE 287 | 288 | Ecrire un enum façon Java. L'exemple classique 289 | est celui de l'enum Suit, avec les cas hearts, 290 | diamonds, clubs, spades. 291 | 292 | */ 293 | 294 | //TODO : Ecrire la classe Suit ici 295 | -------------------------------------------------------------------------------- /source/en/01basics.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A program is just a toplevel function with no 4 | parameters. To run the program, select the 5 | function name and go to: 6 | 7 | - In IntelliJ: 8 | 9 | Run > Run 10 | 11 | (or just click the green arrow) 12 | 13 | - In Eclipse: 14 | 15 | Run > Run As > Ceylon Java Application 16 | 17 | */ 18 | 19 | shared void hello() { 20 | print("Hello, World!"); 21 | } 22 | 23 | /* 24 | 25 | EXERCISE 26 | 27 | You probably want to know what print() does! 28 | You can view its implementation by holding 29 | down the ctrl or command key while clicking 30 | the name of the function to navigate to its 31 | declaration. 32 | 33 | */ 34 | 35 | /* 36 | 37 | hello() and print() are examples of toplevel 38 | functions - we don't need an instance of an 39 | object to invoke them. 40 | 41 | A toplevel function may accept arguments and 42 | return a value, in which case we must specify 43 | the types of the parameters, and the type of 44 | value it returns. 45 | 46 | A parameter may have a default value. 47 | 48 | */ 49 | 50 | String greeting(String name = "World") { 51 | //interpolated expressions are enclosed 52 | //in double-backticks within a string 53 | return "Hello, ``name``!"; 54 | } 55 | 56 | /* 57 | 58 | When a function just returns an expression, 59 | we can abbreviate it using a fat arrow. 60 | 61 | */ 62 | 63 | shared void helloName() => print(greeting("Ceylon")); 64 | 65 | shared void helloWorld() => print(greeting()); 66 | 67 | /* 68 | 69 | A parameter may be variadic, meaning it 70 | accepts multiple values. 71 | 72 | */ 73 | 74 | Integer sum(Integer* numbers) { 75 | variable value sum = 0; //assignable values must be annotated variable 76 | for (x in numbers) { 77 | sum+=x; 78 | } 79 | return sum; 80 | } 81 | 82 | shared void calculateSums() { 83 | 84 | //the sum of no numbers 85 | print(sum()); 86 | 87 | //the sum of two numbers 88 | print(sum(2, 2)); 89 | 90 | //the sum of all the numbers from 0 to 10 91 | //inclusive, using the range operator .. 92 | //and the spread operator * 93 | print(sum(*(0..10))); 94 | 95 | //just to whet your appetite, the sum of 96 | //all the square numbers from 0 to 100! 97 | //the ^ operator performs exponentiation 98 | print(sum(for (n in 0..10) n^2)); 99 | 100 | } 101 | 102 | /* 103 | 104 | Variables are called "values" in Ceylon, 105 | because they're not actually variable 106 | by default! 107 | 108 | */ 109 | 110 | shared void greet() { 111 | String greeting = "hei verden"; 112 | //TODO: use the IDE to fill in the rest! 113 | } 114 | 115 | /* 116 | 117 | EXERCISE 118 | 119 | Fill in the rest of this function. No, we 120 | don't want you to write a trivial call to 121 | print() by hand. We want you to let the 122 | IDE do it for you: 123 | 124 | - Type part of the name of the function 125 | you want to call. 126 | 127 | - ctrl-space activates autocompletion. 128 | 129 | - Selecting a function puts you into linked 130 | mode where you can quickly fill in the 131 | arguments. Use tab to navigate between 132 | them. 133 | 134 | - esc or a typed character outside the 135 | linked mode fields exits linked mode. 136 | 137 | */ 138 | 139 | /* 140 | 141 | A value may be a constant (within a certain 142 | scope), a variable, or a getter. 143 | 144 | */ 145 | 146 | //a toplevel constant 147 | Integer zero = 0; 148 | 149 | //a variable with its initial value 150 | variable Integer int = zero; 151 | 152 | //a getter defined using a fat arrow 153 | Integer intSquared => int^2; 154 | 155 | //a getter defined using a block 156 | Integer intFactorial { 157 | variable value fac = 1; 158 | for (i in 1..int) { 159 | fac*=i; 160 | } 161 | return fac; 162 | } 163 | 164 | shared void values() { 165 | int = 3; 166 | print("i = ``int``"); 167 | print("i^2 = ``intSquared``"); 168 | print("i! = ``intFactorial``"); 169 | int = 4; 170 | print("i = ``int``"); 171 | print("i^2 = ``intSquared``"); 172 | print("i! = ``intFactorial``"); 173 | } 174 | 175 | /* 176 | 177 | A local declaration may have its type 178 | inferred by the compiler. Hover over the 179 | keyword value or function to see the inferred 180 | type of the declaration. 181 | 182 | The type of a function parameter cannot be 183 | inferred. 184 | 185 | */ 186 | 187 | shared void inferredTypes() { 188 | value time = system.milliseconds; 189 | value nl = operatingSystem.newline; 190 | function sqr(Float float) => float*float; 191 | } 192 | 193 | /* 194 | 195 | EXERCISE 196 | 197 | Position the caret over a value or function 198 | keyword and: 199 | 200 | - In IntelliJ: 201 | 202 | click the yellow lightbulb icon that appears 203 | 204 | - In Eclipse: 205 | 206 | select Source > Quick Fix/Assist 207 | 208 | And then select the 'Declare explicit type' 209 | quick assist from the popup menu. 210 | 211 | Or, in Eclipse only, select the whole function, 212 | and select: 213 | 214 | Source > Reveal Inferred Types 215 | 216 | */ 217 | 218 | 219 | /* 220 | 221 | Unicode character escapes are really useful. 222 | For example, pi can be written \{#03C0}. 223 | 224 | Ooops, the Eclipse console is braindead! 225 | Go to: 226 | 227 | Project > Properties > Resource 228 | 229 | and set your text file encoding to UTF-8 230 | before running this program. 231 | 232 | */ 233 | 234 | shared void helloPi() => print(greeting("\{#03C0}")); 235 | 236 | /* 237 | 238 | Or, much more verbose, but also much more 239 | understandable, we can use the Unicode 240 | character name. 241 | 242 | */ 243 | 244 | shared void helloPi2() 245 | => print(greeting("\{GREEK SMALL LETTER PI}")); 246 | 247 | 248 | /* 249 | 250 | What if we want to actually literally print 251 | the string "\{#03C0}"? We can use a backslash 252 | escape, or a verbatim string. 253 | 254 | */ 255 | 256 | shared void printTheUnicodeEscapesForPi() { 257 | 258 | //the escape \\ is a literal backslash 259 | print("\\{#03C0}"); 260 | 261 | //triple-double-quotes delimit a verbatim 262 | //string with no escape characters 263 | print("""\{GREEK SMALL LETTER PI}"""); 264 | 265 | } 266 | 267 | /* 268 | 269 | String literals may span multiple lines. We 270 | especially use multi-line string literals to 271 | specify API documentation in markdown format. 272 | 273 | You can view the formatted documentation of 274 | the following function by: 275 | 276 | - In IntelliJ: 277 | 278 | selecting the name of the function and 279 | View > Quick Documentation 280 | 281 | 282 | - In Eclipse: 283 | 284 | hovering over the name of the function 285 | 286 | */ 287 | 288 | "This program prints: 289 | 290 | - the _name of the virtual machine,_ 291 | - the _version of the virtual machine,_ and 292 | - the _version of the Ceylon language._ 293 | 294 | It uses the [[operatingSystem]] and [[language]] 295 | objects defined in `ceylon.language`, the 296 | Ceylon language module." 297 | shared void printInfo() => 298 | print("operating system: ``operatingSystem.name`` 299 | version: ``operatingSystem.version`` 300 | language: ``language.version`` (``language.versionName``)"); 301 | //hint: try using the normalized 302 | // attribute of String 303 | 304 | /* 305 | 306 | Annotations specify metadata about a program 307 | element. Check out the documentation of this 308 | function. 309 | 310 | */ 311 | 312 | by ("Gavin") 313 | throws (`class Exception`) 314 | deprecated ("Well, this is not a very useful 315 | program. Try [[hello]] instead.") 316 | see (`function hello`) 317 | shared void failNoisily() { 318 | throw Exception("aaaaarrrrrggggghhhhhhh"); 319 | } 320 | 321 | /* 322 | 323 | EXERCISE 324 | 325 | Write a program that prints out all the prime 326 | numbers between 1 and 100. Remember to 327 | properly document your work! 328 | 329 | */ 330 | 331 | //TODO: print out the prime numbers! 332 | -------------------------------------------------------------------------------- /source/es/05iterables_secuencias.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Ya conocimos el tipo Iterable. Normalmente 4 | escribimos: 5 | 6 | - {T*} para indicar un iterable de cero o 7 | más instancias de T 8 | - {T+} para indicar un iterable de una o 9 | más instancias de T 10 | 11 | De hecho, estas abreviaciones simplemente 12 | significan Iterable e Iterable 13 | respectivamente. 14 | 15 | Y por supuesto, {T+} es un subtipo de {T*}. 16 | 17 | Se puede construir un Iterable usando llaves. 18 | 19 | */ 20 | 21 | {String*} noStrings = {}; 22 | {String+} twoStrings = {"hello", "world"}; 23 | {String+} manyStrings = { for (n in 0..100) n.string }; 24 | 25 | /* 26 | 27 | Iterable define bastantes métodos para trabajar 28 | con flujos de valores. Por ejemplo, los famosos 29 | métodos map(), filter() y fold(). 30 | 31 | */ 32 | 33 | shared void demoMapFilterFold() { 34 | print((1..100) 35 | .filter((i) => i%3==0) 36 | .map((i) => i^2) 37 | //TODO: reemplazar fold() con String.join() 38 | .fold("")((partial, ii) 39 | => partial + ", " + ii.string)); 40 | } 41 | 42 | /* 43 | 44 | EJERCICIO 45 | 46 | Limpiar el código anterior usando String.join(). 47 | 48 | */ 49 | 50 | /* 51 | 52 | El código anterior es bastante ruidoso. Es 53 | mucho más común usar comprensiones para 54 | expresar este tipo de cosas. 55 | 56 | */ 57 | 58 | shared void demoComprehension() { 59 | value squares = { 60 | for (i in 1..100) 61 | if (i%3==0) 62 | (i^2).string 63 | }; 64 | print(", ".join(squares)); 65 | } 66 | 67 | /* 68 | 69 | Una comprensión puede tener varias cláusulas 70 | de for y de if. 71 | 72 | */ 73 | 74 | {Character*} ss = { 75 | for (arg in process.arguments) 76 | for (c in arg) 77 | if (c.uppercase) 78 | c.uppercased 79 | }; 80 | 81 | /* 82 | 83 | Por lo tanto, en muchos casos tenemos dos 84 | maneras distintas de expresar algo: 85 | 86 | - usando funciones anónimas, o 87 | - usando una comprensión. 88 | 89 | */ 90 | 91 | Boolean allNumbers1 92 | = manyStrings.every((s) 93 | => !Integer.parse(s) is ParseException); 94 | 95 | Boolean allNumbers2 96 | = every { 97 | for (s in manyStrings) 98 | !Integer.parse(s) is ParseException 99 | }; 100 | 101 | /* 102 | 103 | Una secuencia es una lista inmutable de valores 104 | con una longitud finita. Los tipos secuencia se 105 | escriben [T*] o [T+]. Y para no romper con la 106 | tradición, [T*] también se puede escribir como T[]. 107 | 108 | De hecho, lo anterior son simples abreviaciones para 109 | Sequential y Sequence. 110 | 111 | Podemos construir una secuencia usando corchetes. 112 | 113 | */ 114 | 115 | [String*] noStringsSeq = []; 116 | [String+] twoStringsSeq = ["hello", "world"]; 117 | [String+] manyStringsSeq = [ for (n in 0..100) n.string ]; 118 | 119 | /* 120 | 121 | La secuencia vacía [] es de tipo Empty, que se 122 | escribe []. 123 | 124 | */ 125 | 126 | [] emptySeq = []; 127 | 128 | /* 129 | 130 | Podemos acceder a los elementos de una secuencia 131 | (o cualquier otro subtipo de List) usando 132 | el operador de índice. 133 | 134 | */ 135 | 136 | shared void testSequenceIndexing() { 137 | 138 | //El operador de índice con un solo elemento 139 | //resulta en un tipo posiblemente nulo, 140 | //por tanto no hay IndexOutOfBoundsException. 141 | assert(exists world = twoStringsSeq[1], 142 | world=="world"); 143 | 144 | //Los operadores de índice con rango cerrado 145 | //y abierto resultan en una secuencia. 146 | assert(manyStringsSeq[1..2]==["1", "2"]); 147 | assert(manyStringsSeq[99...]==["99", "100"]); 148 | 149 | } 150 | 151 | /* 152 | Podemos acotar una secuencia posiblemente vacía 153 | (una [T*]) a una secuencia no vacía (una [T+]) 154 | usando el operador nonempty. 155 | */ 156 | 157 | shared void demoNonempty() { 158 | if (nonempty args = process.arguments) { 159 | //Pon el cursor sobre args y first 160 | //para ver sus tipos! 161 | value first = args.first; 162 | print(first); 163 | } 164 | } 165 | 166 | /* 167 | 168 | Se pueden iterar los índices y los elementos 169 | de una secuencia (o cualquier otro subtipo 170 | de List). 171 | 172 | */ 173 | 174 | shared void demoForWithIndexes() { 175 | for (i->s in twoStringsSeq.indexed) { 176 | print("``i`` -> ``s``"); 177 | } 178 | } 179 | 180 | /* 181 | 182 | Las tuplas son un tipo especial de secuencia: 183 | es una lista ligada y con tipos. Para 184 | especificar un tipo tupla se listan los tipos 185 | de los elementos en corchetes, y una tupla 186 | se crea simplemente listando sus elementos 187 | entre corchetes. 188 | 189 | */ 190 | 191 | [Float, Integer, String, String] tuple = 192 | [0.0, 22, "hello", "world"]; 193 | 194 | /* 195 | 196 | Los elementos de una tupla se pueden 197 | extraer sin perder ninguna información 198 | de tipo. 199 | 200 | */ 201 | 202 | shared void testTupleIndexing() { 203 | Null nil1 = tuple[-1]; 204 | Float float = tuple[0]; 205 | Integer int = tuple[1]; 206 | String string1 = tuple[2]; 207 | String string2 = tuple[3]; 208 | Null nil2 = tuple[4]; 209 | } 210 | 211 | /* 212 | 213 | De hecho, todo esto es simplemente azúcar 214 | para la clase Tuple. Siempre utilizamos 215 | el azúcar en este caso; nunca queremos 216 | escribir lo siguiente: 217 | 218 | */ 219 | 220 | shared void desugaredTuple() { 221 | Tuple> pair 222 | = Tuple(1.0,Tuple("hello",[])); 223 | Float float = pair.first; 224 | String string = pair.rest.first; 225 | Null nil = pair.rest.rest.first; 226 | } 227 | 228 | /* 229 | 230 | EJERCICIO 231 | 232 | ¡Revisa la declaración de la clase Tuple 233 | para entender cómo funciona todo esto! 234 | 235 | */ 236 | 237 | /* 238 | 239 | Podemos utilizar el operador desplegar para 240 | pasar una tupla conteniendo argumentos a una 241 | función. Recuerda que un tipo función consta 242 | de un tipo de retorno y un tipo de tupla que 243 | representa los tipos de los parámetros. Asi, 244 | la tupla de argumentos tiene que ser asignable 245 | al tipo de tupla de los parámetros. 246 | 247 | */ 248 | 249 | shared void demoSpreadTuple() { 250 | value args = [(Character c)=>!c.letter, true]; 251 | for (word in "Hello, World! Goodbye.".split(*args)) { 252 | print(word); 253 | } 254 | } 255 | 256 | /* 257 | 258 | Podemos usar tuplas para definir funciones con 259 | múltiples valores de retorno. 260 | 261 | */ 262 | 263 | //una función que produce una tupla 264 | [String, String?, String] parseName(String name) { 265 | value it = name.split().iterator(); 266 | "first name is required" 267 | assert (is String first = it.next()); 268 | "last name is required" 269 | assert (is String second = it.next()); 270 | if (is String third = it.next()) { 271 | return [first, second, third]; 272 | } 273 | else { 274 | return [first, null, second]; 275 | } 276 | } 277 | 278 | /* 279 | 280 | El operador desplegar y la función unflatten() 281 | nos ayudan a componer este tipo de funciones. 282 | 283 | */ 284 | 285 | //una función con múltiples parámetros 286 | String welcome(String first, String? middle, String last) => 287 | "Welcome, ``first`` ``last``!"; 288 | 289 | shared void demoFunctionComposition() { 290 | //el operador * "esparce" la tupla 291 | //resultado de parseName() sobre los 292 | //parámetros de welcome() 293 | print(welcome(*parseName("John Doe"))); 294 | 295 | //pero ¿y si queremos componer parseName() 296 | //y welcome() sin proporcionar los 297 | //argumentos por adelantado? Bien, podemos 298 | //usar compose() y unflatten() 299 | value greet = compose(print, 300 | compose(unflatten(welcome), parseName)); 301 | greet("Jane Doe"); 302 | 303 | //así que en realidad podríamos reescribir el 304 | //primer ejemplo en términos de unflatten() 305 | print(unflatten(welcome)(parseName("Jean Doe"))); 306 | } 307 | -------------------------------------------------------------------------------- /source/de/02klassen.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Jeder Wert ist eine Instanz einer Klasse. Die 4 | einfachsten Klassen gruppieren einfach zusammen- 5 | gehörige Zustände in Attributen. 6 | 7 | Klassen haben Member: 8 | 9 | - Parameter, 10 | - Methoden (Member-Funktionen), 11 | - Attribute (Member-Werte), und 12 | - Member-Klassen. 13 | 14 | Jeder Member, der mit "shared" annotiert ist, 15 | ist Teil der API der Klasse und von außerhalb 16 | der Klasse zugänglich. 17 | 18 | Für einfache Klassen müssen wir normalerweise 19 | die Member string, equals() und hash verfeinern, 20 | die von der Standard-Superklasse Basic geerbt werden. 21 | 22 | Ein Member, der mit "actual" annotiert ist, verfeinert 23 | einen Member der Superklasse. 24 | 25 | */ 26 | 27 | class Time(shared Integer hour, 28 | shared Integer minute) { 29 | 30 | shared Integer secondsSinceMidnight => 31 | minute%60*60 + hour*60*60; 32 | 33 | shared actual Integer hash => 34 | secondsSinceMidnight; 35 | 36 | shared actual Boolean equals(Object that) { 37 | // "is Time" überprüft den Typ des Werts 38 | // und schränkt ihn ein 39 | if (is Time that) { 40 | // hier hat "that" den Typ Time 41 | return secondsSinceMidnight == 42 | that.secondsSinceMidnight; 43 | } 44 | else { 45 | return false; 46 | } 47 | } 48 | 49 | // Falls du keine Lust hast, 50 | // "shared actual Type" zu schreiben, kannst 51 | // du die folgende, abgekürzte Syntax verwenden: 52 | string => "``hour``:``minute``"; 53 | 54 | } 55 | 56 | shared void tryOutTime() { 57 | Time time1 = Time(13,30); 58 | print(time1); 59 | Time time2 = Time(37,30); 60 | print(time2); 61 | Time time3 = Time(13,29); 62 | print(time2); 63 | print(time1==time2); 64 | print(time1==time3); 65 | print(time1.secondsSinceMidnight); 66 | } 67 | 68 | /* 69 | 70 | Um Klassen zu testen, sind Assertions sehr 71 | nützlich. Lasse diese Funktion laufen, um 72 | eine fehlgeschlagene Assertion zu sehen. 73 | 74 | */ 75 | 76 | // TODO: Assertion-Fehler beheben 77 | shared void testTime() { 78 | Time time1 = Time(13,30); 79 | assert (time1.string=="13:30"); 80 | Time time2 = Time(37,30); 81 | assert (time2.string=="37:30"); 82 | Time time3 = Time(13,29); 83 | assert (time3.string=="13:29"); 84 | assert (time1==time2); 85 | assert (time1!=time3); 86 | assert (time1.secondsSinceMidnight==48600); 87 | } 88 | 89 | /* 90 | 91 | ÜBUNG 92 | 93 | Passe die Time-Klasse so an, dass alle Assertions 94 | erfolgreich sind. 95 | 96 | */ 97 | 98 | /* 99 | 100 | Der Zustand mancher Klassen ist veränderlich. 101 | Wenn ein Attribut veränderlich ist, muss es 102 | "variable" annotiert sein. 103 | 104 | */ 105 | 106 | class Counter(count=0) { 107 | shared variable Integer count; 108 | shared void inc() => count++; 109 | string => count.string; 110 | } 111 | 112 | shared void testCounter() { 113 | value counter = Counter(); 114 | assert (counter.count==0); 115 | counter.inc(); 116 | counter.inc(); 117 | assert (counter.count==2); 118 | counter.count = 0; 119 | assert (counter.count==0); 120 | } 121 | 122 | /* 123 | 124 | ÜBUNG 125 | 126 | Füge eine reset()-Methode zu Counter hinzu. 127 | 128 | Wenn du willst, kannst du die IDE fast die ganze 129 | Arbeit für dich machen lassen. Entferne zunächst 130 | die Kommentarzeichen aus dem Code unten. Die 131 | IDE wird einen Fehler anzeigen, und wenn du den 132 | Mauszeiger über den Fehler hältst, oder 133 | 134 | Edit > Quick Fix / Assist 135 | 136 | verwendest, während der Cursor auf dem Fehler ist, 137 | kann dir die IDE eine teilweise Korrektur vorschlagen. 138 | 139 | */ 140 | 141 | // TODO: diesen Test wieder reinkommentieren 142 | // und zum Funktionieren bringen 143 | //shared void testReset() { 144 | // value counter = Counter(); 145 | // assert (counter.count==0); 146 | // counter.inc(); 147 | // counter.inc(); 148 | // assert (counter.count==2); 149 | // counter.reset(); 150 | // assert (counter.count==0); 151 | //} 152 | 153 | /* 154 | 155 | Eine Unterklasse kann unsere Klasse ableiten 156 | und ihre Member verfeinern. Ein Member kann 157 | verfeinert werden, wenn er default annotiert ist. 158 | 159 | */ 160 | 161 | class SecondTime(Integer hour, 162 | Integer minute, 163 | second) 164 | extends Time(hour, minute) { 165 | 166 | // Wir können den Typ und die Annotationen 167 | // eines Parameters im Körper der Klasse 168 | // deklarieren, damit die Parameterliste 169 | // aufgräumter aussieht. 170 | shared Integer second; 171 | 172 | /* 173 | 174 | Ups - der folgende Code ist fehlerhaft! 175 | 176 | Kommentiere ihn zunächst wieder rein, und 177 | behebe dann den Fehler, indem du eine 178 | default-Annotation zu Time.secondsSinceMidnight 179 | (oben) hinzufügst. 180 | 181 | */ 182 | 183 | // TODO: reinkommentieren, Fehler beheben! 184 | //secondsSinceMidnight => 185 | // super.secondsSinceMidnight+second%60; 186 | 187 | } 188 | 189 | /* 190 | 191 | ÜBUNG 192 | 193 | Behebe die Fehler in Time und SecondTime, 194 | so dass die folgenden Tests erfolgreich durchlaufen. 195 | 196 | */ 197 | 198 | // TODO: Assertion-Fehler beheben 199 | shared void testSecondTime() { 200 | Time time = Time(13,30); 201 | assert (time.string=="13:30"); 202 | SecondTime stime1 = SecondTime(13,30,00); 203 | SecondTime stime2 = SecondTime(13,30,25); 204 | assert (time==stime1); 205 | assert (time!=stime2); 206 | assert (stime1.string=="13:30:00"); 207 | assert (stime2.string=="13:30:25"); 208 | } 209 | 210 | /* 211 | 212 | Eine anonyme Klassendeklaration definiert 213 | eine Instanz. Damit wird gleichzeitig eine 214 | Klasse und ihr einziger Wert deklariert. 215 | 216 | */ 217 | 218 | object midnight extends SecondTime(0,0,0) { 219 | // TODO: reinkommentieren, Fehler beheben 220 | //string => "midnight"; 221 | } 222 | 223 | /* 224 | 225 | Eine abstrakte Klasse ist eine Klasse, die 226 | nicht instanziiert werden kann. Sie kann 227 | Member deklarieren, die "formal" annotiert sind 228 | und dann von konkreten Unterklassen implementiert 229 | werden müssen. 230 | 231 | Ein aufgezählter Typ ist eine abstrakte Klasse oder 232 | eine Schnittstelle, die ihre Untertypen aufzählt 233 | und damit einschränkt. 234 | 235 | */ 236 | 237 | abstract class LinkedList() 238 | of Cons | empty { 239 | shared formal Integer length; 240 | } 241 | 242 | // Dieser Fall des aufgezählten Typs ist eine Klasse 243 | class Cons(shared T first, 244 | shared LinkedList rest) 245 | extends LinkedList() { 246 | length=>rest.length+1; 247 | } 248 | 249 | // Dieser Fall des aufgezählten Typs ist ein Singleton-Objekt 250 | object empty 251 | extends LinkedList() { 252 | length=>0; 253 | } 254 | 255 | String formatLinkedList(LinkedList list) { 256 | // Wir verwenden ein switch-Statement, 257 | // um die verschiedenen Fälle des aufgezählten 258 | // Typs abzuhandeln. Der Compiler stellt sicher, 259 | // dass alle Fälle behandelt werden. 260 | switch (list) 261 | case (empty) { 262 | return ""; 263 | } 264 | case (is Cons) { 265 | value rest = list.rest; 266 | value firstString = list.first.string; 267 | switch (rest) 268 | case (empty) { 269 | return firstString; 270 | } 271 | else { 272 | return firstString + ", " + 273 | formatLinkedList(rest); 274 | } 275 | } 276 | } 277 | 278 | shared void testLinkedList() { 279 | value list = Cons("Smalltalk", Cons("Java", Cons("Ceylon", empty))); 280 | assert (list.length==3); 281 | print(formatLinkedList(list)); 282 | } 283 | 284 | /* 285 | 286 | ÜBUNG 287 | 288 | Schreibe eine Java-mäßige enum in Ceylon. Das 289 | klassische Beispiel sind die Farben einer Spielkarte: 290 | Kreuz, Pik, Herz, Karo. 291 | 292 | */ 293 | 294 | // TODO: Schreibe hier eine Farbe-Klasse 295 | -------------------------------------------------------------------------------- /source/es/03null_union_interseccion.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet 3 | } 4 | /* 5 | 6 | Un tipo unión representa una elección entre 7 | varios tipos. Un tipo unión se escribe A|B 8 | para los tipos A y B. Se pronuncia "A o B". 9 | 10 | */ 11 | 12 | void printDouble(String|Integer|Float val) { 13 | String|Integer|Float double; 14 | switch (val) 15 | case (is String) { double = val+val; } 16 | case (is Integer) { double = 2*val; } 17 | case (is Float) { double = 2.0*val; } 18 | print("double ``val`` is ``double``"); 19 | } 20 | 21 | shared void testDouble() { 22 | printDouble("hello"); 23 | printDouble(111); 24 | printDouble(0.111); 25 | } 26 | 27 | /* 28 | 29 | Manejamos los valores ausentes o "nulos" 30 | usando tipos unión. La clase Null tiene una 31 | única instancia llamada null, que representa 32 | un valor ausente. Por lo tanto, una cadena 33 | que posiblemente sea nula se representa 34 | con el tipo Null|String. 35 | 36 | Podemos escribir el tipo "Null|String" 37 | usando la abreviación sintáctica "String?". 38 | Esto es simplemente azúcar para la unión 39 | de un tipo con Null. 40 | 41 | El tipo "String?" se pronuncia "string quizá". 42 | 43 | Corre el siguiente programa con y sin un 44 | argumento de línea de comando. Puedes indicar 45 | un argumento de línea de comando usando: 46 | 47 | Run > Run Configurations... 48 | 49 | */ 50 | 51 | shared void helloArguments() { 52 | String? name = process.arguments[0]; 53 | if (is String name) { //TODO: usar exists 54 | print("hello " + name); 55 | } 56 | else { 57 | print("hello world"); 58 | } 59 | } 60 | 61 | /* 62 | 63 | EJERCICIO 64 | 65 | Normalmente usamos el operador "exists" en 66 | vez de "is String" en código como el anterior. 67 | Modifica tu programa para que use exists. 68 | ¡Más azúcar sintáctica! 69 | 70 | Ahora, para hacer el código un poco más 71 | compacto, modifícalo para que use esta forma 72 | de if: 73 | 74 | if (is String name = process.arguments[0]) 75 | 76 | (Pero usando "exists", no "is String".) 77 | 78 | */ 79 | 80 | /* 81 | 82 | Los operadores then y else producen y 83 | consumen valores nulos. 84 | 85 | */ 86 | 87 | shared void thenAndElse() { 88 | Integer n = 5; 89 | 90 | print(n>0 then n); 91 | print(n<=0 then n); 92 | 93 | print(n>=0 then "positive" else "strictly negative"); 94 | 95 | print("123456789"[n] else "out of bounds"); 96 | print("12345"[n] else "out of bounds"); 97 | } 98 | 99 | /* 100 | 101 | EJERCICIO 102 | 103 | Cambia el programa helloArguments() para que 104 | utilice un operador en vez del if. 105 | 106 | */ 107 | 108 | /* 109 | 110 | Una intersección representa la combinación de 111 | dos tipos. La intersección de los tipos A y B 112 | se escribe A&B. Se pronuncia "A y B". 113 | 114 | Los tipos intersección generalmente surgen como 115 | resultado de acotación de tipos. 116 | 117 | Nota: La sintaxis {T*} significa una secuencia 118 | iterable de valores de tipo T. Es azúcar 119 | sintáctica para la interfaz Iterable. 120 | 121 | */ 122 | 123 | T? third({T*} iterable) { 124 | if (is Correspondence iterable) { 125 | //Pon el cursor sobre iterable para ver 126 | //su tipo acotado dentro de este bloque 127 | return iterable[2]; 128 | } 129 | else { 130 | value iterator = iterable.iterator(); 131 | iterator.next(); 132 | iterator.next(); 133 | if (is T third = iterator.next()) { 134 | return third; 135 | } 136 | else { 137 | return null; 138 | } 139 | } 140 | } 141 | 142 | shared void testThird() { 143 | assert (exists thrd = third("hello"), 144 | thrd =='l'); 145 | } 146 | 147 | /* 148 | 149 | Cabe resaltar que el tipo de thrd aquí arriba 150 | es &Object, que se expande a 151 | Null&Object|Character&Object, que se simplifica 152 | como Nothing|Character, que a su vez se simplifica 153 | más como Character. El verificador de tipos 154 | (typechecker) realiza todo este razonamiento 155 | de manera automática. 156 | 157 | Los tipos unión e intersección son especialmente 158 | útiles a la hora de hacer inferencia de tipos. 159 | 160 | */ 161 | 162 | shared void demoTypeInference() { 163 | //Pon el cursor sobre joined para ver su tipo 164 | value joined = concatenate("hello", 1..69); 165 | Object[] objects = joined; 166 | print(objects); 167 | } 168 | 169 | /* 170 | 171 | Los tipos unión e intersección también 172 | ayudan a definir correctamente los tipos 173 | de las operaciones de unión e intersección 174 | en los conjuntos. 175 | 176 | La unión e intersección de conjuntos se definen 177 | en término de los métodos intersection() y 178 | union() en la interfaz Set. Los operadores 179 | | y & son azúcar sintáctica para estos 180 | métodos. 181 | 182 | Puedes ver la definición de estos métodos en 183 | la documentación de Set para ver cómo a su vez 184 | se definen en términos de la unión/intersección 185 | de sus tipos. 186 | 187 | */ 188 | 189 | shared void demoSets() { 190 | Set chars = HashSet { elements = "hello"; }; 191 | Set ints = HashSet { elements = 0..10; }; 192 | //pon el cursor sobre intsAndChars para ver su tipo 193 | value intsAndChars = chars|ints; 194 | print(intsAndChars); 195 | //pon el cursor sobre empty para ver su tipo 196 | value empty = chars&ints; 197 | print(empty); 198 | } 199 | 200 | /* 201 | 202 | EJERCICIO 203 | 204 | El tipo especial Nothing es el tipo "fondo", un 205 | tipo que no tiene instancias. 206 | 207 | Explica por qué el tipo de la intersección 208 | chars&ints en el código anterior es un Set, 209 | dado que los tipos Character e Integer son 210 | tipos disjuntos (no tienen instancias en común). 211 | 212 | P.D. Character e Integer son clases finales, 213 | no relacionadas por herencia, y por lo tanto 214 | disjuntas. 215 | 216 | */ 217 | 218 | /* 219 | 220 | Espera, si Nothing no tiene valores, ¿cuál es el 221 | significado del siguiente código correctamente 222 | tipado? 223 | 224 | */ 225 | 226 | shared void thereIsNoNothing() { 227 | Nothing n = nothing; 228 | print(n); 229 | } 230 | 231 | /* 232 | 233 | Bien, el tipo Nothing indica, en la práctica, 234 | que la invocación de una función o la evaluación 235 | de un valor: 236 | 237 | - no termina, o 238 | - lanza una excepción. 239 | 240 | Podría parecer que Nothing no es demasiado útil, 241 | pero ayuda un montón cuando se definen tipos 242 | genéricos y funciones genéricas. 243 | 244 | */ 245 | 246 | /* 247 | 248 | La función coalesce() es muy útil y demuestra 249 | una buena aplicación de intersecciones. 250 | 251 | Revisa la definición de coalesce() para ver 252 | cómo funciona. 253 | 254 | */ 255 | 256 | shared void demoCoalesce() { 257 | 258 | //{String?*} es el tipo de un iterable de 259 | //cadenas y nulos 260 | {String?*} stringsAndNulls = {"hello", null, "world"}; 261 | 262 | //{String*} es el tipo de un iterable de 263 | //puras cadenas (sin nulos) 264 | {String*} strings = stringsAndNulls.coalesced; 265 | 266 | assert (strings.sequence() == ["hello", "world"]); 267 | 268 | } 269 | 270 | /* 271 | 272 | Hay un truco especial que se puede hacer con tipos 273 | unión que nos ayuda a darle el tipo correcto a 274 | funciones como max() y min(). 275 | 276 | El problema es que cuando tenemos "cero o más" 277 | cosas, max() puede devolver null. Pero cuando 278 | tenemos "una o más" cosas, nunca devolverá null. 279 | Y cuando tenemos exactamente cero cosas, max() 280 | siempre devolverá null. ¿Cómo se puede capturar 281 | esto dentro del sistema de tipos? 282 | 283 | */ 284 | 285 | shared void demoMax() { 286 | 287 | Null maxOfZero = max({}); 288 | 289 | {Integer+} oneOrMore = {1, 2}; 290 | Integer maxOfOneOrMore = max(oneOrMore); 291 | 292 | {Integer*} zeroOrMore = {1, 2}; 293 | Integer? maxOfZeroOrMore = max(zeroOrMore); 294 | 295 | } 296 | 297 | /* 298 | 299 | EJERCICIO 300 | 301 | Revisa las definiciones de max() e 302 | Iterable para descifrar cómo es que lo 303 | anterior funciona... 304 | 305 | */ 306 | -------------------------------------------------------------------------------- /source/fr/05iterables_sequences.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Nous avons déjà rencontré le type Iterable. Généralement 4 | nous écrivons : 5 | 6 | - {T*} pour indiquer un iterable de zéro ou plus 7 | instances de T 8 | - {T+} pour indiquer un iterable de une ou plus 9 | instances de T 10 | 11 | En réalité, ces abréviations signifient respectivement 12 | Iterable et Iterable. 13 | 14 | Evidemment, {T+} est un sous-type de {T*}. 15 | 16 | Nous pouvons construire des itérables en utilisant 17 | des accolades. 18 | 19 | */ 20 | 21 | {String*} noStrings = {}; 22 | {String+} twoStrings = {"hello", "world"}; 23 | {String+} manyStrings = { for (n in 0..100) n.string }; 24 | 25 | /* 26 | 27 | Iterable définit de nombreuses méthodes pour 28 | manipuler les flux de données. 29 | 30 | */ 31 | 32 | shared void demoMapFilterFold() { 33 | print((1..100) 34 | .filter((i) => i%3==0) 35 | .map((i) => i^2) 36 | //TODO : remplacez fold() par String.join() 37 | .fold("")((partial, ii) 38 | => partial + ", " + ii.string)); 39 | } 40 | 41 | /* 42 | 43 | EXERCISE 44 | 45 | Nettoyez le code ci-dessus en utilisant String.join(). 46 | 47 | */ 48 | 49 | /* 50 | 51 | Le code ci-dessus est particulièrement bruyant. Il 52 | est bien plus habituel d'utiliser des compréhensions 53 | pour exprimer la même chose. 54 | 55 | */ 56 | 57 | shared void demoComprehension() { 58 | value squares = { 59 | for (i in 1..100) 60 | if (i%3==0) 61 | (i^2).string 62 | }; 63 | print(", ".join(squares)); 64 | } 65 | 66 | /* 67 | 68 | Une compréhension peut avoir plusieurs clauses for 69 | et if. 70 | 71 | */ 72 | 73 | {Character*} ss = { 74 | for (arg in process.arguments) 75 | for (c in arg) 76 | if (c.uppercase) 77 | c.uppercased 78 | }; 79 | 80 | /* 81 | 82 | Nous avons souvent le choix entre deux manières 83 | d'exprimer la même chose : 84 | 85 | - en utilisant des fonctions anonymes, ou bien 86 | - en utilisant une compréhension 87 | 88 | */ 89 | 90 | Boolean allNumbers1 91 | = manyStrings.every((s) 92 | => !Integer.parse(s) is ParseException); 93 | 94 | Boolean allNumbers2 95 | = every { 96 | for (s in manyStrings) 97 | !Integer.parse(s) is ParseException 98 | }; 99 | 100 | /* 101 | 102 | Une séquence est une liste immuable de valeurs de 103 | longueur finie. Les types de séquence sont écrits 104 | [T*] ou [T+]. Pour respecter la tradition, nous 105 | sommes également autorisés à écrire [T*] en tant 106 | que T[]. 107 | 108 | En fait ce sont simplement des abréviations pour 109 | Sequential et Sequence. 110 | 111 | Nous pouvons construire une séquence en utilisant 112 | des crochets. 113 | 114 | */ 115 | 116 | [String*] noStringsSeq = []; 117 | [String+] twoStringsSeq = ["hello", "world"]; 118 | [String+] manyStringsSeq = [ for (n in 0..100) n.string ]; 119 | 120 | /* 121 | 122 | La séquence vide [] est de type Empty, que nous 123 | écrivons []. 124 | 125 | */ 126 | 127 | [] emptySeq = []; 128 | 129 | /* 130 | 131 | Nous pouvons accéder à un élément de la séquence 132 | (ou tout autre type de List) en utilisant l'opérateur 133 | d'index. 134 | 135 | */ 136 | 137 | shared void testSequenceIndexing() { 138 | 139 | //l'opérateur d'index simple 140 | //peut produire un type possiblement null ! 141 | //(il n'y a pas de IndexOutOfBoundsException) 142 | assert(exists world = twoStringsSeq[1], 143 | world=="world"); 144 | 145 | //l'opérateur d'intervalle ouvert et fermé 146 | //produit une séquence 147 | assert(manyStringsSeq[1..2]==["1", "2"]); 148 | assert(manyStringsSeq[99...]==["99", "100"]); 149 | 150 | } 151 | 152 | /* 153 | Nous pouvons restreindre une séquence 154 | potentiellement vide (une [T*]) à une 155 | séquence non vide (une [T+]) en utilisant 156 | l'opérateur nonempty. 157 | */ 158 | 159 | shared void demoNonempty() { 160 | if (nonempty args = process.arguments) { 161 | //survolez args et first pour voir 162 | //leur type 163 | value first = args.first; 164 | print(first); 165 | } 166 | } 167 | 168 | /* 169 | 170 | Nous pouvons itérer les indexes et éléments d'une 171 | séquence (ou tout autre type de List). 172 | 173 | */ 174 | 175 | shared void demoForWithIndexes() { 176 | for (i->s in twoStringsSeq.indexed) { 177 | print("``i`` -> ``s``"); 178 | } 179 | } 180 | 181 | /* 182 | 183 | Les Tuples sont un type spécial de séquence : 184 | une liste chainée typée. Les types de Tuples sont 185 | définis en listant les types des éléments entre 186 | crochets, et un tuple est créé en listant ses éléments 187 | entre crochets. 188 | 189 | */ 190 | 191 | [Float, Integer, String, String] tuple 192 | = [0.0, 22, "hello", "world"]; 193 | 194 | /* 195 | 196 | Les éléments d'une tuple peuvent être retrouvés 197 | sans perte d'information de typage. 198 | 199 | */ 200 | 201 | shared void demoTupleIndexing() { 202 | Null nil1 = tuple[-1]; 203 | Float float = tuple[0]; 204 | Integer int = tuple[1]; 205 | String string1 = tuple[2]; 206 | String string2 = tuple[3]; 207 | Null nil2 = tuple[4]; 208 | } 209 | 210 | /* 211 | 212 | En fait, tout ceci est simplement du sucre 213 | syntaxique pour la classe Tuple. Nous utilisons 214 | systématiquement le sucre dans ce cas ; nous ne 215 | voulons certainement pas écrire la chose suivante : 216 | 217 | */ 218 | 219 | shared void desugaredTuple() { 220 | Tuple> pair 221 | = Tuple(1.0,Tuple("hello",[])); 222 | Float float = pair.first; 223 | String string = pair.rest.first; 224 | Null nil = pair.rest.rest.first; 225 | } 226 | 227 | /* 228 | 229 | EXERCICE 230 | 231 | Allez voir la déclaration de Tuple pour comprendre 232 | comment tout cela fonctionne. 233 | 234 | */ 235 | 236 | /* 237 | 238 | Nous pouvons utiliser l'opérateur d'expansion pour 239 | passer un tuple contenant des arguments à une 240 | fonction. Vous vous Souvenez qu'une fonction est 241 | composée d'un type de retour et d'un type de 242 | tuple encodant les types de paramètres ? Et bien, 243 | le tuple des arguments auquel est appliqué 244 | l'opérateur d'expansion doit être assignable 245 | à ce type de tuple. 246 | 247 | 248 | */ 249 | 250 | shared void demoSpreadTuple() { 251 | value args = [(Character c) => !c.letter, true]; 252 | for (word in "Hello, World! Goodbye.".split(*args)) { 253 | print(word); 254 | } 255 | } 256 | 257 | /* 258 | 259 | Nous pouvons utiliser des tuples pour définir des 260 | fonctions avec de valeurs de retour multiples. 261 | 262 | */ 263 | 264 | //une fonction qui produit un tuple 265 | [String, String?, String] parseName(String name) { 266 | value it = name.split().iterator(); 267 | "first name is required" 268 | assert (is String first = it.next()); 269 | "last name is required" 270 | assert (is String second = it.next()); 271 | if (is String third = it.next()) { 272 | return [first, second, third]; 273 | } 274 | else { 275 | return [first, null, second]; 276 | } 277 | } 278 | 279 | /* 280 | 281 | L'opérateur d'expansion et la fonction unflatten() 282 | aident à composer ce type de fonction. 283 | 284 | */ 285 | 286 | //une fonction avec des paramètres multiples 287 | String welcome(String first, String? middle, String last) => 288 | "Welcome, ``first`` ``last``!"; 289 | 290 | shared void demoFunctionComposition() { 291 | //l'opérateur * expand le tuple résultant 292 | //de parseName() sur les paramètres de 293 | //welcome() 294 | print(welcome(*parseName("John Doe"))); 295 | 296 | //Mais comment faire si nous voulons composer 297 | //parseName() et welcome() sans fournir d'arguments 298 | //à l'avance ? Et bien nous pouvons utiliser compose() 299 | //et unflatten() 300 | value greet = compose(print, 301 | compose(unflatten(welcome), parseName)); 302 | greet("Jane Doe"); 303 | 304 | //Donc nous pourrions en fait reformuler le premier 305 | //exemple uniquement en utilisant unflatten() 306 | print(unflatten(welcome)(parseName("Jean Doe"))); 307 | } -------------------------------------------------------------------------------- /source/de/05aufzählungen_folgen.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Wir sind dem Typ Iterable für Aufzählungen 4 | bereits begegnet. Meistens schreiben wir: 5 | 6 | - {T*} für eine Aufzählung von null oder 7 | mehr Instanzen von T 8 | - {T+} für eine Aufzählung von einem oder 9 | mehr Instanzen von T 10 | 11 | Diese Abkürzungen stehen für Iterable 12 | und Iterable. 13 | 14 | Natürlich ist {T+} ein Untertyp von {T*}. 15 | 16 | Wir können eine Aufzählung mit geschweiften 17 | Klammern erstellen. 18 | 19 | */ 20 | 21 | {String*} noStrings = {}; 22 | {String+} twoStrings = { "Hallo", "Welt" }; 23 | {String+} manyStrings = { for (n in 0..100) n.string }; 24 | 25 | /* 26 | 27 | Iterable definiert eine große Anzahl Methoden, 28 | um mit Datenströmen zu arbeiten. Zum Beispiel 29 | hat es die bekannten Methoden map(), filter(), 30 | und fold(). 31 | 32 | */ 33 | 34 | shared void demoMapFilterFold() { 35 | print((1..100) 36 | .filter((i) => i%3==0) 37 | .map((i) => i^2) 38 | // TODO: fold() mit String.join() ersetzen 39 | .fold("")((partial, ii) 40 | => partial + ", " + ii.string)); 41 | } 42 | 43 | /* 44 | 45 | ÜBUNG 46 | 47 | Räum den obigen Code durch Verwendung von 48 | String.join() etwas auf. 49 | 50 | */ 51 | 52 | /* 53 | 54 | Der obige Code ist schwer zu verstehen. 55 | Normalerweise verwenden wir für so etwas 56 | eher Comprehensions. 57 | 58 | */ 59 | 60 | shared void demoComprehension() { 61 | value squares = { 62 | for (i in 1..100) 63 | if (i%3==0) 64 | (i^2).string 65 | }; 66 | print(", ".join(squares)); 67 | } 68 | 69 | /* 70 | 71 | Eine Comprehension kann mehrere for- und 72 | if-Klauseln haben. 73 | 74 | */ 75 | 76 | {Character*} ss = { 77 | for (arg in process.arguments) 78 | for (c in arg) 79 | if (c.uppercase) 80 | c.uppercased 81 | }; 82 | 83 | /* 84 | 85 | Oft haben wir also die Wahl zwischen zwei 86 | verschiedenen Ausdrucksweisen: 87 | 88 | - anonyme Funktionen, oder 89 | - Comprehensions. 90 | 91 | */ 92 | 93 | Boolean allNumbers1 94 | = manyStrings.every((s) 95 | => !Integer.parse(s) is ParseException); 96 | 97 | Boolean allNumbers2 98 | = every { 99 | for (s in manyStrings) 100 | !Integer.parse(s) is ParseException 101 | }; 102 | 103 | /* 104 | 105 | Eine Folge (Sequence) ist eine unveränderliche, 106 | endlich lange Liste von Werten. Folgentypen 107 | werden als [T*] oder [T+] abgekürzt; aus 108 | traditionellen Gründen ist für [T*] auch 109 | die Schreibweise T[] erlaubt und gebräuchlich. 110 | 111 | Dabei handelt es sich lediglich um Abkürzungen 112 | für Sequential und Sequence. 113 | 114 | Wir können eine Folge mit eckigen Klammern erstellen. 115 | 116 | */ 117 | 118 | [String*] noStringsSeq = []; 119 | [String+] twoStringsSeq = ["Hallo", "Welt"]; 120 | [String+] manyStringsSeq = [for (n in 0..100) n.string]; 121 | 122 | /* 123 | 124 | Die leere Folge [] hat den Typ Empty, den wir 125 | ebenfalls als [] schreiben. 126 | 127 | */ 128 | 129 | [] emptySeq = []; 130 | 131 | /* 132 | 133 | Wir können auf die Elemente einer Folge (oder 134 | einer beliebigen anderen Liste) mit dem Index- 135 | Operator zugreifen. 136 | 137 | */ 138 | 139 | shared void testSequenceIndexing() { 140 | 141 | // Zugriff über einen einzelnen Index 142 | // liefert einen möglicherweise fehlenden Wert! 143 | // (Es gibt keine IndexOutOfBoundsException.) 144 | assert(exists world = twoStringsSeq[1], 145 | world=="world"); 146 | 147 | // Die Operatoren für Zugriff auf 148 | // abgeschlossene und offene Intervalle 149 | // ergeben eine Folge 150 | assert(manyStringsSeq[1..2]==["1", "2"]); 151 | assert(manyStringsSeq[99...]==["99", "100"]); 152 | 153 | } 154 | 155 | /* 156 | 157 | Wir können eine möglicherweise leere Folge 158 | ([T*]) mit dem nonempty-Operator zu einer 159 | garantiert nicht leeren Folge machen. 160 | 161 | */ 162 | 163 | shared void demoNonempty() { 164 | if (nonempty args = process.arguments) { 165 | // Halte den Mauszeiger über args 166 | // und first, um ihren Typ zu sehen! 167 | value first = args.first; 168 | print(first); 169 | } 170 | } 171 | 172 | /* 173 | 174 | Wir können über die Indexe und Elemente 175 | einer Folge (oder einer beliebigen anderen 176 | Liste) iterieren. 177 | 178 | */ 179 | 180 | shared void demoForWithIndexes() { 181 | for (i->s in twoStringsSeq.indexed) { 182 | print("``i`` -> ``s``"); 183 | } 184 | } 185 | 186 | /* 187 | 188 | Tupel sind eine besondere Art von Folgen: 189 | Getypte verkettete Listen. Tupel-Typen 190 | werden als Auflistung der Elementtypen in 191 | eckigen Klammern notiert, und ein Tupel 192 | wird erstellt, indem seine Elemente 193 | in eckigen Klammern aufgezählt werden. 194 | 195 | */ 196 | 197 | [Float, Integer, String, String] tuple 198 | = [0.0, 22, "Hallo", "Welt"]; 199 | 200 | /* 201 | 202 | Es ist möglich, auf Elemente eines Tupels 203 | zuzugreifen, ohne Information über ihren 204 | Typ zu verlieren. 205 | 206 | */ 207 | 208 | shared void demoTupleIndexing() { 209 | Null nil1 = tuple[-1]; 210 | Float float = tuple[0]; 211 | Integer int = tuple[1]; 212 | String string1 = tuple[2]; 213 | String string2 = tuple[3]; 214 | Null nil2 = tuple[4]; 215 | } 216 | 217 | /* 218 | 219 | In Wirklichkeit ist das alles nur syntaktischer 220 | Zucker für die Tuple-Klasse. Hier verwenden wir 221 | immer den syntaktischen Zucker; wir wollen nie 222 | das hier schreiben: 223 | 224 | */ 225 | 226 | shared void desugaredTuple() { 227 | Tuple> pair 228 | = Tuple(1.0,Tuple("Hallo",[])); 229 | Float float = pair.first; 230 | String string = pair.rest.first; 231 | Null nil = pair.rest.rest.first; 232 | } 233 | 234 | /* 235 | 236 | ÜBUNG 237 | 238 | Schau dir die Deklaration von Tuple an, 239 | um zu verstehen, wie das funktioniert! 240 | 241 | */ 242 | 243 | /* 244 | 245 | Wir können den Verteilungs-Operator (spread-Operator) 246 | verwenden, um ein Tupel von Argumenten an eine Funktion 247 | zu übergeben. Weißt du noch, dass ein Funktionstyp 248 | einen Rückgabetyp und einen Tupel-Typ, der die 249 | Parametertypen enthält, hat? Der Typ des verteilten 250 | Tupels muss zu diesem Tupel-Typ zuweisbar sein, 251 | d. h. ein Untertyp davon sein. 252 | 253 | */ 254 | 255 | shared void demoSpreadTuple() { 256 | value args = [(Character c) => !c.letter, true]; 257 | for (word in "Hallo, Welt! Tschüss.".split(*args)) { 258 | print(word); 259 | } 260 | } 261 | 262 | /* 263 | 264 | Wir können Tupel verwenden, um Funktionen zu 265 | definieren, die mehrere Werte zurückgeben. 266 | 267 | */ 268 | 269 | // Eine Funktion, die ein Tupel produziert 270 | [String, String?, String] parseName(String name) { 271 | value it = name.split().iterator(); 272 | "Vorname erforderlich" 273 | assert (is String first = it.next()); 274 | "Nachname erforderlich" 275 | assert (is String second = it.next()); 276 | if (is String third = it.next()) { 277 | return [first, second, third]; 278 | } 279 | else { 280 | return [first, null, second]; 281 | } 282 | } 283 | 284 | /* 285 | 286 | Der Verteilungs-Operator und die unflatten()- 287 | Funktion helfen uns dabei, solche Funktionen 288 | zu kombinieren. 289 | 290 | */ 291 | 292 | // Eine Funktion mit mehreren Parametern 293 | String welcome(String first, String? middle, String last) => 294 | "Willkommen, ``first`` ``last``!"; 295 | 296 | shared void demoFunctionComposition() { 297 | // Der * Operator verteilt das Tupel aus 298 | // parseName() über die Parameter von welcome() 299 | print(welcome(*parseName("Max Mustermann"))); 300 | 301 | // Aber was ist, wenn wir parseName() und 302 | // welcome() kombinieren wollen, ohne gleich 303 | // Argumente zu übergeben? Dazu können wir 304 | // compose() und unflatten() verwenden. 305 | value greet = compose(print, 306 | compose(unflatten(welcome), parseName)); 307 | greet("Maria Mustermann"); 308 | 309 | // Jetzt könnten wir auch das erste Beispiel 310 | // mit unflatten() umschreiben 311 | print(unflatten(welcome)(parseName("Martina Mustermann"))); 312 | } 313 | -------------------------------------------------------------------------------- /source/fr/01bases.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Un programme est simplement une fonction de 4 | de premier niveau sans paramètres. Pour exécuter 5 | le programme, sélectionnez le nom de la fonction 6 | et utilisez : 7 | 8 | Run > Run As > Ceylon Java Application 9 | 10 | */ 11 | 12 | shared void hello() { 13 | print("Hello, World!"); 14 | } 15 | 16 | /* 17 | 18 | EXERCICE 19 | 20 | Vous souhaitez probablement savoir à quoi 21 | sert print() ! 22 | Vous pouvez pour cela au choix : 23 | - survoler le nom de la fonction afin de voir 24 | sa documentation, ou bien 25 | - garder enfoncée la touche ctrl ou command et 26 | cliquer sur le nom de la fonction pour 27 | naviguer vers sa déclaration. 28 | 29 | */ 30 | 31 | /* 32 | 33 | hello() et print() sont des exemples de 34 | fonction de premier niveau. Nous n'avons 35 | pas besoin d'une instance d'objet pour les 36 | appeler. 37 | 38 | Une fonction de premier niveau peut prendre 39 | des arguments et retourner une valeur, dans 40 | ce cas nous devons indiquer le type des paramètres, 41 | et le type de valeur retournée. 42 | 43 | Un paramètre peu avoir une valeur par défaut. 44 | 45 | */ 46 | 47 | String greeting(String name = "World") { 48 | //Les expressions interpolées sont encadrées 49 | //de doubles guillemets obliques à l'intérieur 50 | //des chaînes de caractère. 51 | return "Hello, ``name``!"; 52 | } 53 | 54 | /* 55 | 56 | Quand une fonction retourne simplement 57 | une expression, nous pouvons l'abréger 58 | avec une grosse flèche. 59 | 60 | */ 61 | 62 | shared void helloName() => print(greeting("Ceylon")); 63 | 64 | shared void helloWorld() => print(greeting()); 65 | 66 | /* 67 | 68 | Un paramètre peut être variadique, c'est 69 | à dire accepter des valeurs multiples. 70 | 71 | */ 72 | 73 | Integer sum(Integer* numbers) { 74 | variable value sum = 0; //les valeurs modifiables doivent être annotées 75 | for (x in numbers) { 76 | sum+=x; 77 | } 78 | return sum; 79 | } 80 | 81 | shared void calculateSums() { 82 | 83 | //une somme sans nombres 84 | print(sum()); 85 | 86 | //une somme de deux nombres 87 | print(sum(2, 2)); 88 | 89 | //la somme de tous les nombres de 0 à 10 90 | //inclusif, en utilisant l'opérateur d'intervalle .. 91 | //et l'opérateur d'expansion * 92 | print(sum(*(0..10))); 93 | 94 | //Et simplement pour vous mettre en appétit, 95 | //la somme de tous les carrés des nombres de 96 | // 0 à 100 ! 97 | //l'opérateur ^ correspond à l'exponentiation. 98 | print(sum(for (n in 0..10) n^2)); 99 | 100 | } 101 | 102 | /* 103 | 104 | Les variables sont appelées "valeurs" en Ceylon, 105 | car elles ne sont en fait pas variables par 106 | défaut ! 107 | 108 | */ 109 | 110 | shared void greet() { 111 | String greeting = "hei verden"; 112 | //TODO : Utilisez l'IDE pour remplir le reste 113 | } 114 | 115 | /* 116 | 117 | EXERCICE 118 | 119 | Complétez cette fonction. Non, nous ne cherchons 120 | pas à vous faire écrire manuellement un simple 121 | appel à print(). Nous souhaitons que vous 122 | laissiez l'IDE le faire pour vous: 123 | 124 | - Saisissez une partie du nom de la fonction 125 | que vous voulez appeler. 126 | 127 | - ctrl-espace active l'autocompletion. 128 | 129 | - Sélectionner une fonction vous fait basculer 130 | en mode lié, dans lequel vous pouvez rapidement 131 | saisir les arguments. Utilisez tabulation 132 | pour naviguer entre eux. 133 | 134 | - echap ou bien le fait de taper un caractère 135 | en dehors d'un champ du mode lié, vous fait 136 | quitter le mode lié. 137 | 138 | */ 139 | 140 | /* 141 | 142 | Une valeur peut être constante (dans une portée 143 | donnée), une variable, ou bien un getter. 144 | 145 | */ 146 | 147 | //une constante de premier niveau 148 | Integer zero = 0; 149 | 150 | //une variable avec sa valeur initiale 151 | variable Integer int = zero; 152 | 153 | //un getter défini en utilisant une grosse flèche 154 | Integer intSquared => int^2; 155 | 156 | //un getter défini en utilisant un block 157 | Integer intFactorial { 158 | variable value fac = 1; 159 | for (i in 1..int) { 160 | fac*=i; 161 | } 162 | return fac; 163 | } 164 | 165 | shared void values() { 166 | int = 3; 167 | print("i = ``int``"); 168 | print("i^2 = ``intSquared``"); 169 | print("i! = ``intFactorial``"); 170 | int = 4; 171 | print("i = ``int``"); 172 | print("i^2 = ``intSquared``"); 173 | print("i! = ``intFactorial``"); 174 | } 175 | 176 | /* 177 | 178 | Le type d'une déclaration locale peut être 179 | inférée par le compilateur. Survolez le mot clé 180 | value ou fonction pour voir le type inféré de 181 | la déclaration. 182 | 183 | Le type d'un paramètre de fonction ne peut pas 184 | être inféré. 185 | 186 | */ 187 | 188 | shared void inferredTypes() { 189 | value time = system.milliseconds; 190 | value nl = operatingSystem.newline; 191 | function sqr(Float float) => float*float; 192 | } 193 | 194 | /* 195 | 196 | EXERCICE 197 | 198 | Positionnez le curseur sur le mot clé value ou 199 | fonction et sélectionnez: 200 | 201 | Source > Quick Fix / Assist 202 | 203 | Puis sélectionnez l'entrée 'Specify type' dans 204 | le menu pop-up. 205 | 206 | Ou bien : sélectionnez l'ensemble de la fonction, et 207 | sélectionnez : 208 | 209 | Source > Reveal Inferred Types 210 | 211 | */ 212 | 213 | 214 | /* 215 | 216 | L'échappement des caractères unicodes est extrêmement 217 | utile. Par exemple, pi peut être écrit \{#03C0}. 218 | 219 | Oups, La console d'Eclipse est stupide! 220 | Allez à: 221 | 222 | Project > Properties > Resource 223 | 224 | et réglez l'encodage de votre fichier texte sur 225 | UTF-8 avant d'exécuter ce programme. 226 | 227 | */ 228 | 229 | shared void helloPi() => print(greeting("\{#03C0}")); 230 | 231 | /* 232 | 233 | Ou encore, plus verbeux, mais également 234 | bien plus lisible, nous pouvons utiliser le 235 | nom de caractère unicode. 236 | 237 | */ 238 | 239 | shared void helloPi2() => print(greeting("\{GREEK SMALL LETTER PI}")); 240 | 241 | 242 | /* 243 | 244 | Comment faire si nous souhaitons littéralement 245 | afficher la chaîne de caractère "\{#03C0}"? Nous 246 | pouvons utiliser un échappement via backslash, 247 | ou une chaîne de caractère verbatim. 248 | 249 | */ 250 | 251 | shared void printTheUnicodeEscapesForPi() { 252 | 253 | //L'échappement \\ est un backslash littéral 254 | print("\\{#03C0}"); 255 | 256 | //les triples-doubles-guillemets délimitent une 257 | //chaîne de caractère verbatim qui n'échappe pas 258 | // les caractères 259 | print("""\{GREEK SMALL LETTER PI}"""); 260 | 261 | } 262 | 263 | /* 264 | 265 | Les chaînes de caractère littérales peuvent 266 | s'étendre sur plusieurs lignes. En particulier 267 | nous les utilisons pour écrire des documentations 268 | d'API en format markdown. Survolez le nom de 269 | cette fonction pour voir sa documentation. 270 | 271 | */ 272 | 273 | "Ce programme affiche: 274 | 275 | - le _nom de la machine virtuelle,_ 276 | - la _version de la machine virtuelle,_ et 277 | - la _version du langage Ceylon._ 278 | 279 | Il utilise les objets [[operatingSystem]] et 280 | [[language]] définis dans `ceylon.language`, le 281 | module du langage Ceylon." 282 | shared void printInfo() => 283 | print("virtual machine: ``operatingSystem.name`` 284 | version: ``operatingSystem.version`` 285 | language: ``language.version`` (``language.versionName``)"); 286 | //conseil: essayez d'utiliser l'attribut 287 | //'normalized' de String 288 | 289 | /* 290 | 291 | Les annotations fournissent des meta-données 292 | concernant les éléments d'un programme. 293 | Survolez le nom de cette fonction. 294 | 295 | */ 296 | 297 | by ("Gavin") 298 | throws (`class Exception`) 299 | deprecated ("Boaf, ce programme n'est pas très 300 | utile. Essayer plutôt [[hello]].") 301 | see (`function hello`) 302 | shared void failNoisily() { 303 | throw Exception("aaaaarrrrrggggghhhhhhh"); 304 | } 305 | 306 | /* 307 | 308 | EXERCICE 309 | 310 | Ecrire un programme qui affiche tous les nombres 311 | premiers entre 1 et 100. N'oubliez pas de documenter 312 | correctement votre travail ! 313 | 314 | */ 315 | 316 | //TODO : écrire les nombres premiers ! 317 | -------------------------------------------------------------------------------- /source/de/01grundlagen.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Ein Programm ist einfach eine Funktion oberster 4 | Ebene ohne Parameter. Um das Programm auszuführen, 5 | wähle den Funktionsnamen aus und starte: 6 | 7 | Run > Run As > Ceylon Java Application 8 | 9 | */ 10 | 11 | shared void hello() { 12 | print("Hallo, Welt!"); 13 | } 14 | 15 | /* 16 | 17 | ÜBUNG 18 | 19 | Du willst vermutlich wissen, was print() macht! 20 | Dazu kannst du: 21 | 22 | - den Mauszeiger über den Funktionsnamen halten, 23 | um die Dokumentation der Funktion zu sehen, oder 24 | - die Strg- bzw. Befehlstaste gedrückt halten und 25 | gleichzeitig den Funktionsnamen klicken, um die 26 | Funktionsdeklaration aufzurufen. 27 | 28 | */ 29 | 30 | /* 31 | 32 | hello() und print() sind Beispiele für Funktionen 33 | oberster Ebene - wir brauchen keine Instanz eines 34 | Objekts, um sie aufzurufen. 35 | 36 | Eine Funktion oberster Ebene kann Argumente entgegen- 37 | nehmen und Werte zurückgeben; in diesem Fall müssen 38 | wir sowohl die Typen der Parameter als auch den Typ 39 | des zurückgegebenen Werts angeben. 40 | 41 | Ein Parameter kann einen Standardwert haben. 42 | 43 | */ 44 | 45 | String greeting(String name = "Welt") { 46 | // Wenn ein String-Literal in doppelte "Backticks" 47 | // eingeschlossene Ausdrücke enthält, werden diese 48 | // ausgewertet und das Ergebnis in den String eingefügt. 49 | return "Hallo, ``name``!"; 50 | } 51 | 52 | /* 53 | 54 | Wenn eine Funktion nur einen Ausdruck zurückgibt, 55 | können wir das mit einem Zuweisungspfeil abkürzen. 56 | 57 | */ 58 | 59 | shared void helloName() => print(greeting("Ceylon")); 60 | 61 | shared void helloWorld() => print(greeting()); 62 | 63 | /* 64 | 65 | Ein Parameter kann variadisch sein, 66 | d. h. mehrere Werte entgegennehmen. 67 | 68 | */ 69 | 70 | Integer sum(Integer* numbers) { 71 | variable value sum = 0; // Veränderliche Werte müssen "variable" annotiert sein 72 | for (x in numbers) { 73 | sum+=x; 74 | } 75 | return sum; 76 | } 77 | 78 | shared void calculateSums() { 79 | 80 | // Leere Summe 81 | print(sum()); 82 | 83 | // Summe zweier Zahlen 84 | print(sum(2, 2)); 85 | 86 | // Die Summe aller Zahlen von 0 bis 10 87 | // (jeweils inklusive), unter Verwendung 88 | // des Bereichs-Operators .. (range-Operator) 89 | // und des Verteilungs-Operators * (spread-Operator) 90 | print(sum(*(0..10))); 91 | 92 | // Um deinen Appetit anzuregen: 93 | // Die Summe aller Quadratzahlen von 0 bis 100! 94 | // Der ^-Operator potenziert Zahlen. 95 | print(sum(for (n in 0..10) n^2)); 96 | 97 | } 98 | 99 | /* 100 | 101 | Variablen heißen in Ceylon "Werte" ("values"), 102 | weil sie standardmäßig gar nicht variabel sind! 103 | 104 | */ 105 | 106 | shared void greet() { 107 | String greeting = "hei verden"; 108 | // TODO: verwende die IDE, um den Rest hinzuzufügen! 109 | } 110 | 111 | /* 112 | 113 | ÜBUNG 114 | 115 | Schreibe den Rest dieser Funktion. Nein, 116 | wir wollen nicht, dass du einen einfachen 117 | print()-Aufruf von Hand schreibst. Wir wollen, 118 | dass du die IDE für dich arbeiten lässt: 119 | 120 | - Tippe einen Teil des Namens derjenigen Funktion, 121 | die du aufrufen willst. 122 | 123 | - Strg+Leertaste löst Autovervollständigung aus. 124 | 125 | - Wenn du eine Funktion auswählst, kommst du in den 126 | verknüpften Modus, wo du die Argumente schnell 127 | einfügen kannst. Mit der Tabulatortaste kannst du 128 | zwischen den verschiedenen Argumenten navigieren. 129 | 130 | - Wenn du die Esc-Taste drückst oder außerhalb 131 | des verknüpften Modus eine Taste drückst, verlässt du 132 | den verknüpften Modus. 133 | 134 | */ 135 | 136 | /* 137 | 138 | Ein Wert kann eine Konstante (im aktuellen Rahmen), 139 | eine Variable oder ein Getter sein. 140 | 141 | */ 142 | 143 | // Eine Konstante oberster Ebene 144 | Integer zero = 0; 145 | 146 | // Eine Variable mit anfänglichem Wert 147 | variable Integer int = zero; 148 | 149 | // Ein Getter, durch einen Zuweisungspfeil definiert 150 | Integer intSquared => int^2; 151 | 152 | // Ein Getter, durch einen Block definiert 153 | Integer intFactorial { 154 | variable value fac = 1; 155 | for (i in 1..int) { 156 | fac*=i; 157 | } 158 | return fac; 159 | } 160 | 161 | shared void values() { 162 | int = 3; 163 | print("i = ``int``"); 164 | print("i^2 = ``intSquared``"); 165 | print("i! = ``intFactorial``"); 166 | int = 4; 167 | print("i = ``int``"); 168 | print("i^2 = ``intSquared``"); 169 | print("i! = ``intFactorial``"); 170 | } 171 | 172 | /* 173 | 174 | Für lokale Deklarationen kann der Compiler 175 | den Wert rückschließen. Halte den Mauszeiger über 176 | das Schlüsselwort "value" oder "function", 177 | um den ermittelten Wert der Deklaration zu sehen. 178 | 179 | Der Typ eines Funktionsparameters kann nicht 180 | ermittelt werden. 181 | 182 | */ 183 | 184 | shared void inferredTypes() { 185 | value time = system.milliseconds; 186 | value nl = operatingSystem.newline; 187 | function sqr(Float float) => float*float; 188 | } 189 | 190 | /* 191 | 192 | ÜBUNG 193 | 194 | Platziere den Cursor über einem Wert oder 195 | einer Funktion und wähle: 196 | 197 | Source > Quick Fix / Assist 198 | 199 | Und wähle dann 'Specify type' aus dem Menü. 200 | 201 | Oder: wähle die ganze Funktion aus, und wähle: 202 | 203 | Source > Reveal Inferred Types 204 | 205 | */ 206 | 207 | 208 | /* 209 | 210 | Unicode-Umschaltcodes sind sehr nützlich. 211 | Zum Beispiel kannst du Pi als \{#03C0} schreiben. 212 | 213 | Dummerweise ist die Eclipse-Konsole zu blöd und wählt 214 | die falsche Kodierung... gehe zu 215 | 216 | Project > Properties > Resource 217 | 218 | und setze deine Kodierung für Textdateien auf UTF-8, 219 | bevor du dieses Programm laufen lässt. 220 | 221 | */ 222 | 223 | shared void helloPi() => print(greeting("\{#03C0}")); 224 | 225 | /* 226 | 227 | Alternativ - der Code wird länger, aber auch 228 | deutlich lesbarer - kannst du auch den vollen 229 | Unicode-Namen des Zeichens verwenden. 230 | 231 | */ 232 | 233 | shared void helloPi2() => print(greeting("\{GREEK SMALL LETTER PI}")); 234 | 235 | 236 | /* 237 | 238 | Was, wenn wir tatsächlich "\{#03C0}" ausgeben 239 | wollen? Wir können eine Backslash-Umschaltsequenz 240 | oder einen wörtlichen String (eine besondere Art 241 | String-Literal) verwenden. 242 | 243 | */ 244 | 245 | shared void printTheUnicodeEscapesForPi() { 246 | 247 | // Die Sequenz \\ steht für einen Backslash 248 | print("\\{#03C0}"); 249 | 250 | // Dreifache doppelte Anführunszeichen 251 | // markieren ein String-Literal, das keine 252 | // Umschaltsequenzen enthält. 253 | print("""\{GREEK SMALL LETTER PI}"""); 254 | 255 | } 256 | 257 | /* 258 | 259 | String-Literale können sich über mehrere 260 | Zeilen erstrecken. Wir verwenden mehrzeilige 261 | String-Literale besonders häufig, um Dokumentation 262 | im Markdown-Format zu schreiben. Halte den Maus- 263 | zeiger über den Namen dieser Funktion, um die 264 | Dokumentation zu sehen. 265 | 266 | */ 267 | 268 | "Dieses Programm gibt folgendes aus: 269 | 270 | - den _Namen der virtuellen Maschine_, 271 | - die _Version der virtuellen Maschine_, und 272 | - die _Version der Ceylon-Programmiersprache_. 273 | 274 | Es verwendet die Objekte [[operatingSystem]] 275 | und [[language]], die im `ceylon.language`-Modul 276 | definiert sind, dem Ceylon-Sprachmodul." 277 | shared void printInfo() => 278 | print("Virtuelle Maschine: ``operatingSystem.name`` 279 | Version: ``operatingSystem.version`` 280 | Sprache: ``language.version`` (``language.versionName``)"); 281 | // Tipp: Probier mal, das "normalized"-Attribut 282 | // von String zu verwenden 283 | 284 | /* 285 | 286 | Annotationen geben Metadaten über ein Programm- 287 | element an. Halte den Mauszeiger über den Namen 288 | dieser Funktion. 289 | 290 | */ 291 | 292 | by ("Gavin") 293 | throws (`class Exception`) 294 | deprecated ("Naja, das ist kein sehr nützliches 295 | Programm. Probier's mal mit [[hello]].") 296 | see (`function hello`) 297 | shared void failNoisily() { 298 | throw Exception("Aaaaarrrrrggggghhhhhhh"); 299 | } 300 | 301 | /* 302 | 303 | ÜBUNG 304 | 305 | Schreibe ein Programm, das alle Primzahlen 306 | zwischen 1 und 100 ausgibt. Denk daran, deine 307 | Arbeit ordentlich zu dokumentieren! 308 | 309 | */ 310 | 311 | // TODO: Primzahlen ausgeben! 312 | -------------------------------------------------------------------------------- /source/fr/03null_unions_intersections.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet 3 | } 4 | /* 5 | 6 | Une union représente un choix possible entre 7 | plusieurs types. Un type union est écrit A|B 8 | pour tous types A et B. Il se lit "A ou B". 9 | 10 | */ 11 | 12 | void printDouble(String|Integer|Float val) { 13 | String|Integer|Float double; 14 | switch (val) 15 | case (is String) { double = val+val; } 16 | case (is Integer) { double = 2*val; } 17 | case (is Float) { double = 2.0*val; } 18 | print("double ``val`` is ``double``"); 19 | } 20 | 21 | shared void testDouble() { 22 | printDouble("hello"); 23 | printDouble(111); 24 | printDouble(0.111); 25 | } 26 | 27 | /* 28 | 29 | Nous gérons les valeurs manquantes ou "null" en 30 | utilisant des types union. La classe Null a une 31 | seule instance nommée null, qui représente une 32 | valeur manquante. Ainsi, une chaîne de caractère 33 | potentiellement manquante est représentée par le 34 | type Null|String. 35 | 36 | Nous pouvons écrire le type "Null|String" en 37 | utilisant la syntaxe d'abréviation "String?". 38 | Il s'agit simplement de sucre syntaxique pour 39 | les types union. Le type "String?" se lit 40 | "String peut-être". 41 | 42 | Lancez le programme suivant d'abord avec, puis 43 | sans un argument de ligne de commande. Vous 44 | pouvez positionner un argument de ligne de 45 | commande en utilisant : 46 | 47 | Run > Run Configurations... 48 | 49 | */ 50 | 51 | shared void helloArguments() { 52 | String? name = process.arguments[0]; 53 | if (is String name) { //TODO: use exists 54 | print("hello " + name); 55 | } 56 | else { 57 | print("hello world"); 58 | } 59 | } 60 | 61 | /* 62 | 63 | EXERCICE 64 | 65 | Nous utilisons généralement l'opérateur "exists" 66 | en lieu et place de "is String" dans ce type 67 | de code. Modifiez le programme ci-dessus afin 68 | d'utiliser exists. Encore du sucre syntaxique ! 69 | 70 | Puis, pour rendre le code un peu plus compact, 71 | modifier le code pour utiliser cette forme 72 | de l'expression if : 73 | 74 | if (is String name = process.arguments[0]) 75 | 76 | (Mais en utilisant "exists", au lieu de 77 | "is String".) 78 | 79 | */ 80 | 81 | /* 82 | 83 | Les opérateurs 'then' and 'else' produisent et 84 | consomment des valeurs null. 85 | 86 | */ 87 | 88 | shared void thenAndElse() { 89 | Integer n = 5; 90 | 91 | print(n>0 then n); 92 | print(n<=0 then n); 93 | 94 | print(n>=0 then "positive" else "strictly negative"); 95 | 96 | print("123456789"[n] else "out of bounds"); 97 | print("12345"[n] else "out of bounds"); 98 | } 99 | 100 | /* 101 | 102 | EXERCICE 103 | 104 | Modifier le programme helloArguments() ci-dessus, 105 | afin d'utiliser un opérateur au lieu de if/else. 106 | 107 | */ 108 | 109 | /* 110 | 111 | Une intersection représente une combinaison 112 | de deux types. Une intersection est écrite 113 | A&B pour tous types A et B. Elle se lit 114 | "A et B". 115 | 116 | Une intersection résulte souvent d'une 117 | restriction de type. 118 | 119 | Note: La syntaxe {T*} représente une séquence 120 | iterable de valeurs de type T. C'est du sucre 121 | syntaxique pour l'interface Iterable. 122 | 123 | */ 124 | 125 | T? third({T*} iterable) { 126 | if (is Correspondence iterable) { 127 | //Survolez iterable pour voir son 128 | //type restreint à l'intérieur de ce block! 129 | return iterable[2]; 130 | } 131 | else { 132 | value iterator = iterable.iterator(); 133 | iterator.next(); 134 | iterator.next(); 135 | if (is T third = iterator.next()) { 136 | return third; 137 | } 138 | else { 139 | return null; 140 | } 141 | } 142 | } 143 | 144 | shared void testThird() { 145 | assert (exists thrd = third("hello"), 146 | thrd =='l'); 147 | } 148 | 149 | /* 150 | 151 | Détail intéressant: le type de 'thrd' ci-dessus 152 | est &Object, qui se développe 153 | en Null&Object|Character&Object, qui se simplifie 154 | en Nothing|Character, qui se simplifie encore 155 | en Character. Le vérificateur de type de Ceylon 156 | fait ce raisonnement automatiquement. 157 | 158 | Les types union et intersection sont particulièrement 159 | utiles pour l'inférence de type. 160 | */ 161 | 162 | shared void demoTypeInference() { 163 | //survolez joined pour voir son type ! 164 | value joined = concatenate("hello", 1..69); 165 | Object[] objects = joined; 166 | print(objects); 167 | } 168 | 169 | /* 170 | 171 | Les types union et intersection aident également 172 | lorsqu'il s'agit de typer correctement les 173 | opérations union et intersection sur des Sets 174 | 175 | Les unions et intersections de Sets sont 176 | définis via les méthodes intersection() et 177 | union() de l'interface Set. Les opérateurs 178 | | et & sont du sucre syntaxique pour ces 179 | méthodes. 180 | 181 | Allez voir les définitions de ces méthodes 182 | dans Set afin de constater qu'elles sont 183 | à leur tour définies en termes de types 184 | union/intersection. 185 | 186 | */ 187 | 188 | shared void demoSets() { 189 | Set chars = HashSet { elements="hello"; }; 190 | Set ints = HashSet { elements=0..10; }; 191 | //survolez intsAndChars pour voir son type! 192 | value intsAndChars = chars|ints; 193 | print(intsAndChars); 194 | //survolez empty pour voir son type! 195 | value empty = chars&ints; 196 | print(empty); 197 | } 198 | 199 | /* 200 | 201 | EXERCICE 202 | 203 | Le type spécial Nothing est le type "bottom" ; 204 | un type sans instance. 205 | 206 | Expliquez pourquoi le type de l'intersection 207 | chars&ints ci-dessus est Set, étant 208 | donné que les types Character et Integer sont 209 | des types disjoints (n'ont pas d'instances en 210 | commun). 211 | 212 | P.S. Character et Integer sont des classes 213 | finales, sans lien d'héritage, et par 214 | conséquent disjoints. 215 | 216 | */ 217 | 218 | /* 219 | 220 | Une seconde ! Si Nothing n'a pas de valeur, quelle 221 | est la signification du code suivant qui est 222 | correctement typé par ailleurs ? 223 | 224 | */ 225 | 226 | shared void thereIsNoNothing() { 227 | Nothing n = nothing; 228 | print(n); 229 | } 230 | 231 | /* 232 | 233 | Et bien, le type Nothing indique en pratique 234 | qu'un appel de fonction ou bien l'évaluation 235 | d'une valeur, au choix : 236 | 237 | - ne se termine pas, ou 238 | - envoie une exception. 239 | 240 | On pourrait croire que Nothing n'est pas très 241 | utile, mais en réalité il aide grandement 242 | lorsqu'il s'agit de définir des types 243 | génériques et des fonctions génériques. 244 | 245 | */ 246 | 247 | /* 248 | 249 | L'attribut 'coalesced' illustre également 250 | une autre utilisation des intersections. 251 | 252 | Allez voir la définition de coalesce() 253 | afin de comprendre son fonctionnement. 254 | 255 | */ 256 | 257 | shared void demoCoalesce() { 258 | 259 | //{String?*} est le type d'un itérable de 260 | //chaînes de caractères et de nulls 261 | {String?*} stringsAndNulls = {"hello", null, "world"}; 262 | 263 | //{String*} est le type d'un itérable de 264 | //chaînes de caractères uniquement (sans nulls) 265 | {String*} strings = stringsAndNulls.coalesced; 266 | 267 | assert (strings.sequence() == ["hello", "world"]); 268 | 269 | } 270 | 271 | /* 272 | 273 | Il existe une astuce avec les types union 274 | qui nous permet de donner aux fonctions comme 275 | max() et min() un typage correcte. 276 | 277 | Le problème vient du fait que quand nous avons 278 | "zéro éléments ou plus", max() peut retourner 279 | null. Mais quand nous avons "un élément ou 280 | plus", ce n'est pas possible. Et quand nous 281 | avons exactement zéro éléments, max() retourne 282 | toujours null. Comment capturer ceci 283 | dans le système de type ? 284 | 285 | */ 286 | 287 | shared void demoMax() { 288 | 289 | Null maxOfZero = max({}); 290 | 291 | {Integer+} oneOrMore = {1, 2}; 292 | Integer maxOfOneOrMore = max(oneOrMore); 293 | 294 | {Integer*} zeroOrMore = {1, 2}; 295 | Integer? maxOfZeroOrMore = max(zeroOrMore); 296 | 297 | } 298 | 299 | /* 300 | 301 | EXERCICE 302 | 303 | Aller voir la définition de max() et Iterable 304 | puis tentez de comprendre comment ceci 305 | fonctionne. 306 | 307 | La solution fait intervenir Rien ;-) 308 | (Nothing) 309 | 310 | */ 311 | -------------------------------------------------------------------------------- /source/de/03null_vereinigung_schnitt.ceylon: -------------------------------------------------------------------------------- 1 | import ceylon.collection { 2 | HashSet 3 | } 4 | /* 5 | 6 | Ein Vereinigungstyp (union type) steht für 7 | eine Auswahlmöglichkeit zwischen mehreren 8 | Typen. Die Vereinigung der Typen A und B 9 | wird als A|B notiert und "A oder B" ausgesprochen. 10 | 11 | */ 12 | 13 | void printDouble(String|Integer|Float val) { 14 | String|Integer|Float double; 15 | switch (val) 16 | case (is String) { double = val+val; } 17 | case (is Integer) { double = 2*val; } 18 | case (is Float) { double = 2.0*val; } 19 | print("Das Doppelte von ``val`` ist ``double``"); 20 | } 21 | 22 | shared void testDouble() { 23 | printDouble("Hallo"); 24 | printDouble(111); 25 | printDouble(0.111); 26 | } 27 | 28 | /* 29 | 30 | Wir behandeln fehlende ("null") Werte durch 31 | Vereinigungstypen. Die Klasse Null hat einen ein- 32 | zigen Wert, null, welcher für einen fehlenden 33 | Wert steht. Damit kann ein möglicherweise fehlen- 34 | der String durch den Typ Null|String beschrieben 35 | werden. 36 | 37 | Wir können den Typ "Null|String" abgekürzt als 38 | "String?" schreiben. Dabei handelt es sich nur 39 | um syntaktischen Zucker für den Vereinigungstyp. 40 | 41 | Der Typ "String?" wird "vielleicht String" 42 | ausgesprochen. 43 | 44 | Lass das folgende Programm einmal mit und einmal 45 | ohne Argumente laufen. Du kannst Argumente hinzu- 46 | fügen unter: 47 | 48 | Run > Run Configurations... 49 | 50 | */ 51 | 52 | shared void helloArguments() { 53 | String? name = process.arguments[0]; 54 | if (is String name) { // TODO: exists verwenden 55 | print("Hallo " + name); 56 | } 57 | else { 58 | print("Hallo Welt"); 59 | } 60 | } 61 | 62 | /* 63 | 64 | ÜBUNG 65 | 66 | Normalerweise verwenden wir den "exists"-Operator, 67 | anstatt wie oben "is String" zu schreiben. Passe 68 | das obige Programm an, so dass exists verwendet wird. 69 | Mehr syntaktischer Zucker! 70 | 71 | Als nächstes kannst du, um den Code noch kompakter 72 | zu machen, diese Form des if-Statements verwenden: 73 | 74 | if (is String name = process.arguments[0]) 75 | 76 | (Nur eben mit exists, nicht "is String".) 77 | 78 | */ 79 | 80 | /* 81 | 82 | Die then- und else-Operatoren erzeugen und 83 | verarbeiten null-Werte. 84 | 85 | */ 86 | 87 | shared void thenAndElse() { 88 | Integer n = 5; 89 | 90 | print(n>0 then n); 91 | print(n<=0 then n); 92 | 93 | print(n>=0 then "Positiv" else "Echt negativ"); 94 | 95 | print("123456789"[n] else "Außer Bereich"); 96 | print("12345"[n] else "Außer Bereich"); 97 | } 98 | 99 | /* 100 | 101 | ÜBUNG 102 | 103 | Passe das helloArguments()-Programm von oben 104 | so an, dass ein Operator anstelle von if/else 105 | verwendet wird. 106 | 107 | */ 108 | 109 | /* 110 | 111 | Ein Schnitttyp (intersection type) steht für 112 | eine Kombination zweier Typen. Der Schnitt der 113 | Typen A und B wird als A&B notiert und "A und B" 114 | ausgesprochen. 115 | 116 | Schnitttypen treten häufig bei der Verfeinerung 117 | von Typen auf. 118 | 119 | Hinweis: Die Syntax {T*} steht für eine Aufzählung 120 | von Werten des Typs T. Dabei handelt es sich um 121 | syntaktischen Zucker für die Iterable-Schnittstelle. 122 | 123 | */ 124 | 125 | T? third({T*} iterable) { 126 | if (is Correspondence iterable) { 127 | // Halte den Mauszeiger über iterable, 128 | // um den verfeinerten Typ innerhalb 129 | // dieses Blocks zu sehen! 130 | return iterable[2]; 131 | } 132 | else { 133 | value iterator = iterable.iterator(); 134 | iterator.next(); 135 | iterator.next(); 136 | if (is T third = iterator.next()) { 137 | return third; 138 | } 139 | else { 140 | return null; 141 | } 142 | } 143 | } 144 | 145 | shared void testThird() { 146 | assert (exists thrd = third("Hallo"), 147 | thrd =='l'); 148 | } 149 | 150 | /* 151 | 152 | Kleine Bemerkung am Rande: Der Typ von "thrd" 153 | oben ist &Object, was zu 154 | Null&Object|Character&Object verteilt wird, 155 | was zu Nothing|Character vereinfacht wird, 156 | welches wiederum zu Character vereinfacht wird. 157 | Das alles macht der Compiler - genauer gesagt, 158 | der "Typechecker", welcher alle Typen überprüft - 159 | automatisch. 160 | 161 | Vereinigungs- und Schnitttypen sind besonders 162 | hilfreich, wenn Typen rückgeschlossen werden. 163 | 164 | */ 165 | 166 | shared void demoTypeInference() { 167 | // Halte den Mauszeiger über joined, 168 | // um den ermittelten Typ zu sehen! 169 | value joined = concatenate("Hallo", 1..69); 170 | Object[] objects = joined; 171 | print(objects); 172 | } 173 | 174 | /* 175 | 176 | Vereinigungs- und Schnitttypen helfen auch 177 | dabei, den korrekten Typ für die Vereinigung 178 | und den Schnitt mehrerer Mengen zu benennen. 179 | 180 | Vereinigung und Schnitt von Mengen sind über 181 | die Methoden union() und intersection() der 182 | Set-Schnittstelle definiert. Die Operatoren 183 | | und & sind syntaktischer Zucker für diese 184 | Methoden. 185 | 186 | Du kannst dir die Definition dieser Methoden 187 | anschauen, um zu sehen, wie ihre Rückgabetypen 188 | durch Vereinigungs- und Schnitttypen definiert 189 | sind. 190 | 191 | */ 192 | 193 | shared void demoSets() { 194 | Set chars = HashSet { elements="Hallo"; }; 195 | Set ints = HashSet { elements=0..10; }; 196 | // Halte den Mauszeiger über intsAndChars, 197 | // um den Typ zu sehen! 198 | value intsAndChars = chars|ints; 199 | print(intsAndChars); 200 | // Halte den Mauszeiger über empty, 201 | // um den Typ zu sehen! 202 | value empty = chars&ints; 203 | print(empty); 204 | } 205 | 206 | /* 207 | 208 | ÜBUNG 209 | 210 | Der spezielle Typ Nothing ("nichts") ist der 211 | "Boden"-Typ, ein Typ ohne Instanzen. 212 | 213 | Überlege dir, warum der Typ der oberen Schnitt- 214 | menge chars&ints Set ist, wenn du weißt, 215 | dass Character und Integer disjunkte Typen sind 216 | (sie haben keine gemeinsamen Instanzen). 217 | 218 | P.S.: Character und Integer sind unableitbare 219 | Klassen (mit final annotiert), nicht über 220 | Vererbung verwandt, und daher disjunkt. 221 | 222 | */ 223 | 224 | /* 225 | 226 | Moment mal. Wenn es keine Werte vom Typ Nothing gibt, 227 | was bedeuted dann der folgende Code? (Alle Typen sind 228 | korrekt.) 229 | 230 | */ 231 | 232 | shared void thereIsNoNothing() { 233 | Nothing n = nothing; 234 | print(n); 235 | } 236 | 237 | /* 238 | 239 | In der Praxis bedeutet der Typ Nothing, dass der 240 | Aufruf einer Funktion, oder das Auswerten eines 241 | Werts, entweder: 242 | 243 | - nie terminiert, oder 244 | - eine Ausnahme wirft. 245 | 246 | Es sieht vielleicht so aus, als wäre Nothing 247 | nutzlos, aber wenn man generische Typen und 248 | Funktionen definiert, kann es sehr hilfreich sein. 249 | 250 | */ 251 | 252 | /* 253 | 254 | Das nützliche coalesced-Attribut demonstriert 255 | auch eine hübsche Anwendung von Schnitttypen. 256 | 257 | Schau dir die Definition von coalesced an, 258 | um zu sehen, wie das funktioniert. 259 | 260 | */ 261 | 262 | shared void demoCoalesce() { 263 | 264 | // {String?*} ist der Typ einer Folge von 265 | // String-Werten und null-Werten 266 | {String?*} stringsAndNulls = {"Hallo", null, "Welt"}; 267 | 268 | // {String*} ist der Typ einer Folge von 269 | // String-Werten (ohne null-Werte) 270 | {String*} strings = stringsAndNulls.coalesced; 271 | 272 | assert (strings.sequence() == ["Hallo", "Welt"]); 273 | 274 | } 275 | 276 | /* 277 | 278 | Es gibt einen speziellen Trick mit Vereinigungs- 279 | typen, der uns hilft, Funktionen wie max() und min() 280 | den korrekten Typ zu geben. 281 | 282 | Das Problem ist, dass bei "null oder mehr" Werten 283 | max() den null-Wert zurückgeben kann. Aber wenn 284 | wir "einen oder mehr" Werte haben, kann das nicht 285 | passieren. Und wenn wir genau null Werte haben, 286 | gibt max() immer den null-Wert zurück. Wie können wir 287 | das im Typsystem darstellen? 288 | 289 | */ 290 | 291 | shared void demoMax() { 292 | 293 | Null maxOfZero = max({}); 294 | 295 | {Integer+} oneOrMore = {1, 2}; 296 | Integer maxOfOneOrMore = max(oneOrMore); 297 | 298 | {Integer*} zeroOrMore = {1, 2}; 299 | Integer? maxOfZeroOrMore = max(zeroOrMore); 300 | 301 | } 302 | 303 | /* 304 | 305 | ÜBUNG 306 | 307 | Schau dir die Definion von max() und Iterable 308 | an und finde heraus, wie das funktioniert! 309 | 310 | Tipp: Nothing (nichts) spielt eine Rolle ;-) 311 | 312 | */ 313 | -------------------------------------------------------------------------------- /source/en/04functions.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | As in most modern languages, a function is a 4 | value. The type of a function is represented 5 | using the interfaces Callable and Tuple, but 6 | we usually hide these interfaces using 7 | syntactic sugar. 8 | 9 | The type of a function is written X(P,Q,R) 10 | where X is the return type, and P, Q, R are 11 | the parameter types. Furthermore: 12 | 13 | - P* means a variadic parameter of type P, and 14 | - P= means a parameter of type P with a default 15 | value. 16 | 17 | A value whose type is a function type is 18 | sometimes called a "function reference". 19 | 20 | */ 21 | 22 | //the return type of a void function is Anything 23 | Anything(Anything) printFun = print; 24 | 25 | //for a parameterized function, type arguments 26 | //must be specified, since a value cannot have 27 | //free parameters 28 | Float(Float, Float) plusFun = plus; 29 | 30 | //sum() has a variadic parameter 31 | Integer(Integer*) sumFun = sum; 32 | 33 | //classes are functions too! 34 | String({Character*}) strFun = String; 35 | 36 | Singleton(Integer) singletonFun 37 | = Singleton; 38 | 39 | //even methods are functions 40 | String({String*}) joinWithCommasFun = ", ".join; 41 | 42 | //split() has defaulted parameters, indicated by = 43 | {String*}(Boolean(Character)=, Boolean=, Boolean=) splitFun 44 | = "Hello, world! Goodbye :-(".split; 45 | 46 | //a "static" reference to an attribute of a type 47 | //is another sort of function! 48 | {Integer*}({Integer?*}) coalesceFun = 49 | Iterable.coalesced; 50 | 51 | /* 52 | 53 | Given a value with a function type, we can do 54 | almost everything we can do with the actual 55 | function. 56 | 57 | (Note: the one thing we can't do is pass named 58 | arguments.) 59 | 60 | */ 61 | 62 | shared void demoFunctionRefs() { 63 | printFun("Hello!"); 64 | print(sumFun(3, 7, 0)); 65 | print(plusFun(3.0, 7.0)); 66 | print(strFun({'h','e','l','l','o'})); 67 | print(singletonFun(0)); 68 | } 69 | 70 | /* 71 | 72 | Function types exhibit the correct variance 73 | with respect to their return and parameter 74 | types. This is just a consequence of the 75 | variance of the type parameters of the types 76 | Callable and Tuple. 77 | 78 | That is, a function with more general 79 | parameter types and a more specific return 80 | type is assignable to a function type. Sounds 81 | complicated, but it all works out pretty much 82 | intuitively. 83 | 84 | */ 85 | 86 | //a function that accepts Anything is also a 87 | //function that accepts a String 88 | Anything(String) printStringFun = printFun; 89 | 90 | //a function that returns a String is also a 91 | //function that returns an Iterable 92 | {Character*}({Character*}) iterableFun1 = strFun; 93 | 94 | //a function that returns a Singleton is also 95 | //a function that returns an Iterable 96 | {Integer+}(Integer) iterableFun2 = singletonFun; 97 | 98 | //a function with a variadic parameter is also 99 | //a function with two parameters! 100 | Integer(Integer, Integer) sumBothFun = sumFun; 101 | 102 | //a function with defaulted parameters works 103 | //like multiple functions with fixed arity. 104 | {String*}() splitOnWhitespaceFun = splitFun; 105 | {String*}(Boolean(Character)) splitOnCharsFun = splitFun; 106 | {String*}(Boolean(Character), Boolean) splitOnCharsDiscardingFun = splitFun; 107 | 108 | /* 109 | 110 | Usually we pass function refs to other 111 | functions. 112 | 113 | */ 114 | 115 | //TODO: change the declaration of op to use a function type 116 | Float apply(Float op(Float x, Float y), Float z) 117 | => op(z/2,z/2); 118 | 119 | shared void testApply() { 120 | assert (apply(plus, 1.0)==1.0); 121 | assert (apply(times, 3.0)==2.25); 122 | } 123 | 124 | /* 125 | 126 | EXERCISE 127 | 128 | The parameter op() of apply() is declared in 129 | "function style", with the parameters listed 130 | after the parameter name, and the return type 131 | first. Change the declaration to use "value 132 | style", with a function type before the 133 | parameter name. 134 | 135 | */ 136 | 137 | /* 138 | 139 | When a function reference to a generic 140 | function occurs as an argument to a 141 | function call, we often don't need to 142 | explicitly specify the type arguments. 143 | 144 | */ 145 | 146 | //TODO: This is a new feature of Ceylon 147 | // 1.1.5, it does not work in the 148 | // current 1.1 release! 149 | //shared void testApplyWithInference() { 150 | // assert (apply(plus, 1.0)==1.0); 151 | // assert (apply(times, 3.0)==2.25); 152 | //} 153 | 154 | /* 155 | 156 | It's even possible to write an "anonymous" 157 | function, inline, within an expression. 158 | 159 | */ 160 | 161 | //TODO: change to a regular function definition 162 | Float(Float, Float) timesFun 163 | = (Float x, Float y) => x*y; 164 | 165 | //TODO: change to a regular function definition 166 | Anything(String) printTwiceFun 167 | = (String s) { 168 | print(s); 169 | print(s); 170 | }; 171 | 172 | /* 173 | 174 | EXERCISE 175 | 176 | What I have just written is very bad style! 177 | The purpose of anonymous functions is to 178 | pass them as arguments to other functions. 179 | Fix the code above by rewriting it using 180 | regular C-style function declaration syntax. 181 | 182 | */ 183 | 184 | /* 185 | 186 | Usually we pass anonymous functions as 187 | arguments to other functions. 188 | 189 | */ 190 | 191 | shared void demoAnonFunction() { 192 | 193 | {String*} result = mapPairs( 194 | (String s, Integer i) 195 | => s.repeat(i), 196 | "well hello world goodbye".split(), 197 | 1..10); 198 | 199 | print(" ".join(result)); 200 | 201 | } 202 | 203 | /* 204 | 205 | In many interesting cases, we can leave 206 | out the parameter types of an anonymous 207 | function, and let them be inferred. 208 | 209 | */ 210 | 211 | shared void demoAnonFunctionParameterInference() { 212 | assert(apply((x, y) => x^y, 4.0)==4.0); 213 | } 214 | 215 | /* 216 | 217 | A "curried" function is a function with 218 | multiple parameter lists. 219 | 220 | */ 221 | 222 | String repeat(Integer times)(String s) 223 | => (" "+s).repeat(times)[1...]; 224 | 225 | shared void demoCurriedFunction() { 226 | String(String) thrice = repeat(3); 227 | print(thrice("hello")); 228 | print(thrice("bye")); 229 | } 230 | 231 | /* 232 | 233 | There's one place we very commonly 234 | encounter functions in curried form: 235 | "static" method references. 236 | 237 | */ 238 | 239 | String({String*})(String) staticJoinFun = String.join; 240 | 241 | shared void testStaticMethodRef() { 242 | value joinWithCommas = staticJoinFun(", "); 243 | value string = joinWithCommas({"hello", "world"}); 244 | print(string); 245 | } 246 | 247 | /* 248 | 249 | Static attribute references are especially useful, 250 | especially in combination with the map() method. 251 | 252 | */ 253 | 254 | shared void testStaticAttributeRef() { 255 | value words = {"hi", "hello", "hola", "jambo"}; 256 | value lengths = words.map(String.size); 257 | print(lengths); 258 | } 259 | 260 | /* 261 | 262 | Because function types are defined in terms 263 | of the perfectly ordinary interfaces Callable 264 | and Tuple, it's possible to write functions 265 | that abstract over many function types at 266 | once. (This is not typically possible in 267 | other languages.) 268 | 269 | The most useful such functions are compose(), 270 | curry(), shuffle(), and uncurry(). These are 271 | perfectly ordinary functions, written entirely 272 | in Ceylon! 273 | 274 | */ 275 | 276 | shared void demoGenericFunctions() { 277 | 278 | //TODO: change this "one-liner" to three lines 279 | value fun = uncurry(compose(curry(plus), 280 | (String s) => parseFloat(s) else 0.0)); 281 | 282 | assert(fun("3.0", 1.0)==4.0); 283 | 284 | } 285 | 286 | /* 287 | 288 | EXERCISE 289 | 290 | The code above is too "compact" to be easily 291 | understood. Use Source > Extract Value to 292 | pull out the subexpressions and see the type 293 | of each. 294 | 295 | */ 296 | 297 | /* 298 | 299 | We've now started to see some pretty 300 | complicated types. To make it easier to deal 301 | with types like these, we can give them names. 302 | 303 | */ 304 | 305 | alias Predicate => Boolean(T); 306 | alias StringPredicate => Predicate; 307 | 308 | Boolean both(Predicate p, T x, T y) 309 | => p(x) && p(y); 310 | 311 | shared void testPredicates() { 312 | 313 | StringPredicate length5 314 | = (String s) => s.size==5; 315 | 316 | assert(both(length5, "hello", "world")); 317 | assert(!both(length5, "goodbye", "world")); 318 | 319 | } 320 | 321 | -------------------------------------------------------------------------------- /source/es/04funciones.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Como en casi cualquier lenguaje moderno, una 4 | función es un valor. El tipo de una función 5 | se representa con las interfaces Callable y Tuple, 6 | pero por lo general ocultamos dichas interfaces 7 | usando azúcar sintáctica. 8 | 9 | El tipo de una función se escribe X(P,Q,R) 10 | donde X es el tipo de retorno y P,Q,R son 11 | los tipos de los parámetros. Además: 12 | 13 | - P* es un parámetro variádico de tipo P, y 14 | - P= es un parámetro de tipo P con un valor 15 | por defecto. 16 | 17 | A un valor cuyo tipo es una función 18 | generalmente lo llamamos "referencia a función". 19 | 20 | */ 21 | 22 | //El tipo de retorno de una función void es Anything 23 | Anything(Anything) printFun = print; 24 | 25 | //Para una función parametrizada, se deben especificar 26 | //los argumentos de tipo, ya que un valor no puede 27 | //tener parámetros libres. 28 | Float(Float, Float) plusFun = plus; 29 | 30 | //sum() tiene un parámetro variádico. 31 | Integer(Integer*) sumFun = sum; 32 | 33 | //¡Las clases también son funciones! 34 | String({Character*}) strFun = String; 35 | 36 | Singleton(Integer) singletonFun = 37 | Singleton; 38 | 39 | //Incluso los métodos son funciones 40 | String({String*}) joinWithCommasFun = ", ".join; 41 | 42 | //split() tiene parámetros por defecto, indicados con = 43 | {String*}(Boolean(Character)=, Boolean=, Boolean=) splitFun 44 | = "Hello, world! Goodbye :-(".split; 45 | 46 | //a "static" reference to an attribute of a type 47 | //is another sort of function! 48 | {Integer*}({Integer?*}) coalesceFun = 49 | Iterable.coalesced; 50 | 51 | /* 52 | 53 | Dado un valor con un tipo función, se puede 54 | hacer casi cualquier cosa que se puede hacer 55 | con la función referida. 56 | 57 | (Nota: lo que no se puede hacer es pasar 58 | argumentos con nombre.) 59 | 60 | */ 61 | 62 | shared void demoFunctionRefs() { 63 | printFun("Hello!"); 64 | print(sumFun(3, 7, 0)); 65 | print(plusFun(3.0, 7.0)); 66 | print(strFun({'h','e','l','l','o'})); 67 | print(singletonFun(0)); 68 | } 69 | 70 | /* 71 | 72 | Los tipos función exhiben la varianza 73 | correcta respecto de sus tipos de retorno 74 | y de parámetros. Esto es tan sólo una 75 | consecuencia de los parámetros de tipo de 76 | Callable y Tuple. 77 | 78 | Es decir, una función con tipos de parámetro 79 | más generales y un tipo de retorno más 80 | específico, es asignable a un tipo función. 81 | Suena complicado, pero de hecho la manera 82 | en que funciona es bastante intuitiva. 83 | 84 | */ 85 | 86 | //Una función que acepta Anything también 87 | //acepta String 88 | Anything(String) printStringFun = printFun; 89 | 90 | //Una función que retorna String también 91 | //es una función que retorna Iterable. 92 | {Character*}({Character*}) iterableFun1 = strFun; 93 | 94 | //Una función que retorna Singleton también 95 | //es una función que retorna Iterable. 96 | {Integer+}(Integer) iterableFun2 = singletonFun; 97 | 98 | //¡Una función con un parámetro variádico también 99 | //es una función que acepta dos parámetros! 100 | Integer(Integer, Integer) sumBothFun = sumFun; 101 | 102 | //Un a función con parámetros por defecto sirve 103 | //como varias funciones de aridad fija. 104 | {String*}() splitOnWhitespaceFun = splitFun; 105 | {String*}(Boolean(Character)) splitOnCharsFun = splitFun; 106 | {String*}(Boolean(Character), Boolean) splitOnCharsDiscardingFun = splitFun; 107 | 108 | 109 | /* 110 | 111 | Generalmente usamos referencias a funciones para 112 | pasarlas a otras funciones. 113 | 114 | */ 115 | 116 | //TODO: cambiar la declaración de op para usar un 117 | //tipo función 118 | Float apply(Float op(Float x, Float y), Float z) 119 | => op(z/2,z/2); 120 | 121 | shared void testApply() { 122 | assert (apply(plus, 1.0)==1.0); 123 | assert (apply(times, 3.0)==2.25); 124 | } 125 | 126 | /* 127 | EJERCICIO 128 | 129 | El parámetro op() de apply() está declarado 130 | en "estilo de función", con sus parámetros 131 | listados después del nombre del parámetros, 132 | y el tipo de retorno al principio. Cambia la 133 | declaración para usar "estilo de valor", 134 | con un tipo función antes del nombre del 135 | parámetro. 136 | 137 | */ 138 | 139 | /* 140 | 141 | Cuando una referencia a función que se 142 | refiere a una función genérica ocurre 143 | como argumento de una llamada de otra 144 | función, no siempre tenemos que especificar 145 | los argumentos de tipos. 146 | 147 | */ 148 | 149 | //TODO: Esto es una nueva characterística 150 | // de Ceylon 1.1.5, no funciona en 151 | // Ceylon 1.1! 152 | //shared void testApplyWithInference() { 153 | // assert (apply(plus, 1.0)==1.0); 154 | // assert (apply(times, 3.0)==2.25); 155 | //} 156 | 157 | /* 158 | 159 | Incluso es posible escribir una función 160 | "anónima", embebida (inline) dentro de una 161 | expresión. 162 | 163 | */ 164 | 165 | //TODO: cambiar la definición de la función a tipo "normal" 166 | Float(Float, Float) timesFun = 167 | (Float x, Float y) => x*y; 168 | 169 | //TODO: Cambiar la definición de la función a tipo "normal" 170 | Anything(String) printTwiceFun = 171 | void (String s) { 172 | print(s); 173 | print(s); 174 | }; 175 | 176 | /* 177 | 178 | EJERCICIO 179 | 180 | ¡Lo que está escrito arriba es un MUY MAL estilo! 181 | El objetivo de las funciones anónimas es pasarlas 182 | como argumentos a otras funciones. 183 | Arregla ese código, reescribiéndolo usando la 184 | sintaxis declarativa normal tipo C. 185 | 186 | */ 187 | 188 | /* 189 | 190 | Por lo general pasamos funciones anónimas 191 | como argumentos a otras funciones. 192 | 193 | */ 194 | 195 | shared void demoAnonFunction() { 196 | 197 | {String*} result = mapPairs( 198 | (String s, Integer i) => 199 | s.repeat(i), 200 | "pues hola mundo adios".split(), 201 | 1..10); 202 | 203 | print(" ".join(result)); 204 | 205 | } 206 | 207 | /* 208 | 209 | En muchos casos interesantes, podemos 210 | omitir los tipos de los parámetros de 211 | una funcion anónima y dejarlos ser 212 | inferidos. 213 | 214 | */ 215 | 216 | shared void demoAnonFunctionParameterInference() { 217 | assert(apply((x, y) => x^y, 4.0)==4.0); 218 | } 219 | 220 | /* 221 | 222 | Una función currificada es una función 223 | con múltiples listas de parámetros. 224 | 225 | */ 226 | 227 | String repeat(Integer times)(String s) => 228 | (" "+s).repeat(times)[1...]; 229 | 230 | shared void demoCurriedFunction() { 231 | String(String) thrice = repeat(3); 232 | print(thrice("hello")); 233 | print(thrice("bye")); 234 | } 235 | 236 | /* 237 | 238 | Hay un sitio en el que encontramos comúnmente 239 | funciones en forma currificada: referencias 240 | "estáticas" a métodos. 241 | 242 | */ 243 | 244 | String({String*})(String) staticJoinFun = String.join; 245 | 246 | shared void testStaticMethodRef() { 247 | value joinWithCommas = staticJoinFun(", "); 248 | value string = joinWithCommas({"hello", "world"}); 249 | print(string); 250 | } 251 | 252 | /* 253 | 254 | Las referencias estáticas a atributos son 255 | especialmente útiles, sobre todo en combinación 256 | con el método map(). 257 | 258 | */ 259 | 260 | shared void testStaticAttributeRef() { 261 | value words = {"hi", "hello", "hola", "jambo"}; 262 | value lengths = words.map(String.size); 263 | print(lengths); 264 | } 265 | 266 | /* 267 | 268 | Debido a que los tipos función se definen 269 | en términos de las interfaces Callable y 270 | Tuple, que son tipos normales de Ceylon, 271 | es posible escribir funciones que abstraen 272 | varios tipos función al mismo tiempo. Esto 273 | generalmente no es posible en otros lenguajes. 274 | 275 | Las funciones más útiles de este tipo son 276 | compose(), curry(), shuffle(), y uncurry(). 277 | ¡Estas son funciones normales, escritas 278 | completamente en Ceylon! 279 | 280 | */ 281 | 282 | shared void demoGenericFunctions() { 283 | 284 | //TODO: cambiar este código de una línea a 285 | //tres líneas 286 | value fun = uncurry(compose(curry(plus), 287 | (String s) => parseFloat(s) else 0.0)); 288 | 289 | assert(fun("3.0", 1.0)==4.0); 290 | 291 | } 292 | 293 | /* 294 | 295 | EJERCICIO 296 | 297 | El código anterior es demasiado compacto para 298 | ser fácilmente entendible. Utiliza la opción 299 | Source > Extract Value para sacar de ahí las 300 | subexpresiones y ver el tipo de cada una. 301 | 302 | */ 303 | 304 | /* 305 | 306 | Ya hemos empezado a ver unos tipos bastante complicados. 307 | Para facilitar el manejo de tipos así, les podemos 308 | dar nombres más simples. 309 | 310 | */ 311 | 312 | alias Predicate => Boolean(T); 313 | alias StringPredicate => Predicate; 314 | 315 | Boolean both(Predicate p, T x, T y) => 316 | p(x) && p(y); 317 | 318 | shared void testPredicates() { 319 | StringPredicate length5 = (String s)=>s.size==5; 320 | assert(both(length5, "hello", "world")); 321 | assert(!both(length5, "goodbye", "world")); 322 | } 323 | -------------------------------------------------------------------------------- /source/de/04funktionen.ceylon: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Wie auch in den meisten modernen Programmier- 4 | sprachen ist eine Funktion in Ceylon ein Wert. 5 | Ihr Typ wird durch die Schnittstellen Callable 6 | und Tuple repräsentiert, die wir aber üblicher- 7 | weise hinter etwas syntaktischem Zucker verstecken. 8 | 9 | Der Typ einer Funktion wird als X(P,Q,R) geschrie- 10 | ben, wobei X der Rückgabewert und P, Q, und R die 11 | Parametertypen sind. Weiterhin bedeutet: 12 | 13 | - P* einen variadischen Parameter vom Typ P, und 14 | - P= einen Parameter vom Typ P mit einem Standard- 15 | wert. 16 | 17 | Ein Wert, dessen Typ ein Funktionstyp ist, wird 18 | manchmal "Funktionsreferenz" genant. 19 | 20 | */ 21 | 22 | // Der Rückgabetyp einer void-Funktion ist Anything 23 | Anything(Anything) printFun = print; 24 | 25 | // Für eine Funktion mit Typparametern müssen Typ- 26 | // argumente angegeben werden, da ein Wert keine 27 | // freien Typparameter haben kann. 28 | Float(Float, Float) plusFun = plus; 29 | 30 | // sum() hat einen variadischen Parameter 31 | Integer(Integer*) sumFun = sum; 32 | 33 | // Klassen sind auch Funktionen! 34 | String({Character*}) strFun = String; 35 | 36 | Singleton(Integer) singletonFun 37 | = Singleton; 38 | 39 | // Selbst Methoden sind Funktionen 40 | String({String*}) joinWithCommasFun = ", ".join; 41 | 42 | // split() has Parameter mit Standardwert, durch = angezeigt 43 | {String*}(Boolean(Character)=, Boolean=, Boolean=) splitFun 44 | = "Hello, world! Goodbye :-(".split; 45 | 46 | // Eine "statische" Referenz auf ein Attribut 47 | // eines Typs ist eine weitere Funktion! 48 | {Integer*}({Integer?*}) coalesceFun = 49 | Iterable.coalesced; 50 | 51 | /* 52 | 53 | Mit einem Wert mit Funktionstyp können wir fast alles 54 | machen, was wir mit der echten Funktion machen können. 55 | 56 | (Das einzige, was nicht geht, ist benannte Argumente 57 | zu übergeben.) 58 | 59 | */ 60 | 61 | shared void demoFunctionRefs() { 62 | printFun("Hallo!"); 63 | print(sumFun(3, 7, 0)); 64 | print(plusFun(3.0, 7.0)); 65 | print(strFun({'H','a','l','l','o'})); 66 | print(singletonFun(0)); 67 | } 68 | 69 | /* 70 | 71 | Funktionstypen haben die korrekte Varianz 72 | in Bezug auf ihre Rückgabe- und Parametertypen. 73 | Das ergibt sich direkt aus der Varianz der 74 | Typparameter der Typen Callable und Tuple. 75 | 76 | Das heißt, eine Funktion mit generelleren 77 | Parametertypen und einem spezielleren Rückgabe- 78 | typ kann einem Funktionstyp zugewiesen werden. 79 | Das klingt kompliziert, funktioniert aber in 80 | der Praxis ziemlich intuitiv. 81 | 82 | */ 83 | 84 | // Eine Funktion, die Anything (irgendwas) 85 | // entgegennimmt, ist auch eine Funktion, die 86 | // einen String entgegennimmt 87 | Anything(String) printStringFun = printFun; 88 | 89 | // Eine Funktion, die einen String zurückgibt, 90 | // ist auch eine Funktion, die eine Aufzählung 91 | // von Zeichen (Characters) zurückgibt 92 | {Character*}({Character*}) iterableFun1 = strFun; 93 | 94 | // Eine Funktion, die ein Singleton (einzelner 95 | // Wert) zurückgibt, ist auch eine Funktion, die 96 | // eine Aufzählung zurückgibt 97 | {Integer+}(Integer) iterableFun2 = singletonFun; 98 | 99 | // Eine Funktion mit einem variadischen Parameter 100 | // ist auch eine Funktion mit zwei Parametern! 101 | Integer(Integer, Integer) sumBothFun = sumFun; 102 | 103 | // Eine Funktion mit Parametern mit Standardwerten 104 | // funktioniert wie mehrere Funktionen mit fester 105 | // Stelligkeit (Anzahl an Parametern, Arität) 106 | {String*}() splitOnWhitespaceFun = splitFun; 107 | {String*}(Boolean(Character)) splitOnCharsFun = splitFun; 108 | {String*}(Boolean(Character), Boolean) splitOnCharsDiscardingFun = splitFun; 109 | 110 | /* 111 | 112 | Üblicherweise geben wir Funktionsreferenzen 113 | an andere Funktionen weiter. 114 | 115 | */ 116 | 117 | // TODO: die Deklaration von op so verändern, dass 118 | // ein Funktionstyp verwendet wird 119 | Float apply(Float op(Float x, Float y), Float z) 120 | => op(z/2,z/2); 121 | 122 | shared void testApply() { 123 | assert (apply(plus, 1.0)==1.0); 124 | assert (apply(times, 3.0)==2.25); 125 | } 126 | 127 | /* 128 | 129 | ÜBUNG 130 | 131 | Der Parameter op() von apply() ist im "funktionalen 132 | Stil" deklariert, wobei die Parameter nach dem 133 | Parameternamen gelistet sind und der Rückgabe- 134 | typ zuerst kommt. Ändere die Deklaration zum "Wert- 135 | Stil", wo der Funktionstyp vor dem Parameternamen steht. 136 | 137 | */ 138 | 139 | /* 140 | 141 | Wenn eine Referenz auf eine generische Funktion 142 | als Argument an einen Funktionsaufruf auftritt, 143 | müssen wir die Typargumente oft nicht explizit 144 | angeben. 145 | 146 | */ 147 | 148 | // TODO: Das ist eine neue Funktionalität aus 149 | // Ceylon 1.1.5, die in der aktuellen 150 | // Version 1.1 noch nicht funktioniert! 151 | //shared void testApplyWithInference() { 152 | // assert (apply(plus, 1.0)==1.0); 153 | // assert (apply(times, 3.0)==2.25); 154 | //} 155 | 156 | /* 157 | 158 | Es ist sogar möglich, eine "anonyme" Funktion 159 | innerhalb eines Ausdrucks zu schreiben. 160 | 161 | */ 162 | 163 | // TODO: zu einer regulären Funktionsdefinition ändern 164 | Float(Float, Float) timesFun 165 | = (Float x, Float y) => x*y; 166 | 167 | // TODO: zu einer regulären Funktionsdefinition ändern 168 | Anything(String) printTwiceFun 169 | = void (String s) { 170 | print(s); 171 | print(s); 172 | }; 173 | 174 | /* 175 | 176 | ÜBUNG 177 | 178 | Was ich da gerade geschrieben habe, ist sehr 179 | schlechter Stil! Anonyme Funktionen sind dazu 180 | gedacht, als Argumente an andere Funktionen 181 | übergeben zu werden. Schreibe den Code oben 182 | so um, dass es sich um reguläre Funktionsdefinitionen 183 | handelt, wie sie aus Java und C bekannt sind. 184 | 185 | */ 186 | 187 | /* 188 | 189 | Normalerweise geben wir anonyme Funktionen 190 | als Argumente an andere Funktionen weiter. 191 | 192 | */ 193 | 194 | shared void demoAnonFunction() { 195 | 196 | {String*} result = mapPairs( 197 | (String s, Integer i) 198 | => s.repeat(i), 199 | "Hallo Welt Tschüss".split(), 200 | 1..10); 201 | 202 | print(" ".join(result)); 203 | 204 | } 205 | 206 | /* 207 | 208 | In vielen interessanten Fällen können wir die 209 | Parametertypen einer anonymen Funktion auslassen 210 | und sie durch den Compiler rückschließen lassen. 211 | 212 | */ 213 | 214 | shared void demoAnonFunctionParameterInference() { 215 | assert (apply((x, y) => x^y, 4.0)==4.0); 216 | } 217 | 218 | /* 219 | 220 | Eine Funktion kann mehrere Parameterlisten haben; 221 | dies wird als "Currying" bezeichnet. 222 | 223 | */ 224 | 225 | String repeat(Integer times)(String s) 226 | => (" "+s).repeat(times)[1...]; 227 | 228 | shared void demoCurriedFunction() { 229 | String(String) thrice = repeat(3); 230 | print(thrice("hello")); 231 | print(thrice("bye")); 232 | } 233 | 234 | /* 235 | 236 | Teilweise angewendete Funktionen treffen 237 | wir besonders häufig an einer Stelle an: 238 | Bei "statischen" Referenzen auf Methoden. 239 | 240 | */ 241 | 242 | String({String*})(String) staticJoinFun = String.join; 243 | 244 | shared void testStaticMethodRef() { 245 | value joinWithCommas = staticJoinFun(", "); 246 | value string = joinWithCommas({"hello", "world"}); 247 | print(string); 248 | } 249 | 250 | /* 251 | 252 | Statische Attribut-Referenzen sind besonders 253 | in Verbindung mit der map()-Methode nützlich. 254 | 255 | */ 256 | 257 | shared void testStaticAttributeRef() { 258 | value words = {"hi", "hello", "hola", "jambo"}; 259 | value lengths = words.map(String.size); 260 | print(lengths); 261 | } 262 | 263 | /* 264 | 265 | Weil Funktionstypen durch die regulären Schnittstellen 266 | Callable und Tuple definiert sind, ist es möglich, 267 | Funktionen zu schreiben, die über viele Funktionstypen 268 | zugleich abstrahieren. (Dies ist in anderen Sprachen 269 | meistens nicht möglich.) 270 | 271 | Die nützlichsten solchen Funktionen sind compose(), 272 | curry(), shuffle() und uncurry(). Das sind ganz 273 | gewöhnliche, in Ceylon geschriebene Funktionen! 274 | 275 | */ 276 | 277 | shared void demoGenericFunctions() { 278 | 279 | // TODO: Diesen "Einzeiler" in drei Zeilen aufteilen 280 | value fun = uncurry(compose(curry(plus), 281 | (String s) => parseFloat(s) else 0.0)); 282 | 283 | assert(fun("3.0", 1.0)==4.0); 284 | 285 | } 286 | 287 | /* 288 | 289 | ÜBUNG 290 | 291 | Der Code oben ist zu kompakt, um leicht verständlich 292 | zu sein. Verwende Source > Extract Value, um 293 | Unterausdrücke herauszuholen und ihren jeweiligen 294 | Typ zu sehen. 295 | 296 | */ 297 | 298 | /* 299 | 300 | Wir haben jetzt schon ein paar ziemlich 301 | komplizierte Typen gesehen. Um den Umgang 302 | mit ihnen zu vereinfachen, können wir sie 303 | benennen. 304 | 305 | */ 306 | 307 | alias Predicate => Boolean(T); 308 | alias StringPredicate => Predicate; 309 | 310 | Boolean both(Predicate p, T x, T y) 311 | => p(x) && p(y); 312 | 313 | shared void testPredicates() { 314 | 315 | StringPredicate length5 316 | = (String s) => s.size==5; 317 | 318 | assert(both(length5, "Hallo", "Welt!")); 319 | assert(!both(length5, "Adieu", "Welt")); 320 | 321 | } 322 | --------------------------------------------------------------------------------