mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-08-14 18:55:00 +02:00
Merge branch 'master' of github.com:geoffliu/learnxinyminutes-docs
This commit is contained in:
@@ -22,6 +22,11 @@ Send a pull request or open an issue any time of day or night.
|
|||||||
**(e.g. [python/en] for English Python).** This will help everyone pick out things they
|
**(e.g. [python/en] for English Python).** This will help everyone pick out things they
|
||||||
care about.
|
care about.
|
||||||
|
|
||||||
|
We're happy for any contribution in any form, but if you're making more than one major change
|
||||||
|
(i.e. translations for two different languages) it would be super cool of you to make a
|
||||||
|
separate pull request for each one so that someone can review them more effectively and/or
|
||||||
|
individually.
|
||||||
|
|
||||||
### Style Guidelines
|
### Style Guidelines
|
||||||
|
|
||||||
* **Keep lines under 80 chars**
|
* **Keep lines under 80 chars**
|
||||||
|
@@ -30,9 +30,11 @@ Multi-line comments don't nest /* Be careful */ // comment ends on this line...
|
|||||||
#define DAYS_IN_YEAR 365
|
#define DAYS_IN_YEAR 365
|
||||||
|
|
||||||
// Enumeration constants are also ways to declare constants.
|
// Enumeration constants are also ways to declare constants.
|
||||||
|
// All statements must end with a semicolon
|
||||||
enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
|
enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
|
||||||
// MON gets 2 automatically, TUE gets 3, etc.
|
// MON gets 2 automatically, TUE gets 3, etc.
|
||||||
|
|
||||||
|
|
||||||
// Import headers with #include
|
// Import headers with #include
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -57,7 +59,6 @@ int main() {
|
|||||||
// print output using printf, for "print formatted"
|
// print output using printf, for "print formatted"
|
||||||
// %d is an integer, \n is a newline
|
// %d is an integer, \n is a newline
|
||||||
printf("%d\n", 0); // => Prints 0
|
printf("%d\n", 0); // => Prints 0
|
||||||
// All statements must end with a semicolon
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// Types
|
// Types
|
||||||
@@ -385,7 +386,8 @@ int main() {
|
|||||||
// or when it's the argument of the `sizeof` or `alignof` operator:
|
// or when it's the argument of the `sizeof` or `alignof` operator:
|
||||||
int arraythethird[10];
|
int arraythethird[10];
|
||||||
int *ptr = arraythethird; // equivalent with int *ptr = &arr[0];
|
int *ptr = arraythethird; // equivalent with int *ptr = &arr[0];
|
||||||
printf("%zu, %zu\n", sizeof arraythethird, sizeof ptr); // probably prints "40, 4" or "40, 8"
|
printf("%zu, %zu\n", sizeof arraythethird, sizeof ptr);
|
||||||
|
// probably prints "40, 4" or "40, 8"
|
||||||
|
|
||||||
|
|
||||||
// Pointers are incremented and decremented based on their type
|
// Pointers are incremented and decremented based on their type
|
||||||
@@ -476,7 +478,7 @@ void testFunc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//make external variables private to source file with static:
|
//make external variables private to source file with static:
|
||||||
static int j = 0; //other files using testFunc() cannot access variable i
|
static int j = 0; //other files using testFunc2() cannot access variable j
|
||||||
void testFunc2() {
|
void testFunc2() {
|
||||||
extern int j;
|
extern int j;
|
||||||
}
|
}
|
||||||
|
@@ -678,6 +678,23 @@ on a new line! ""Wow!"", the masses cried";
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It's also possible to define custom Indexers on objects.
|
||||||
|
// All though this is not entirely useful in this example, you
|
||||||
|
// could do bicycle[0] which yields "chris" to get the first passenger or
|
||||||
|
// bicycle[1] = "lisa" to set the passenger. (of this apparent quattrocycle)
|
||||||
|
private string[] passengers = { "chris", "phil", "darren", "regina" }
|
||||||
|
|
||||||
|
public string this[int i]
|
||||||
|
{
|
||||||
|
get {
|
||||||
|
return passengers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
return passengers[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Method to display the attribute values of this Object.
|
//Method to display the attribute values of this Object.
|
||||||
public virtual string Info()
|
public virtual string Info()
|
||||||
{
|
{
|
||||||
|
@@ -12,7 +12,7 @@ such as Open Firmware. It's also used by NASA.
|
|||||||
Note: This article focuses predominantly on the Gforth implementation of
|
Note: This article focuses predominantly on the Gforth implementation of
|
||||||
Forth, but most of what is written here should work elsewhere.
|
Forth, but most of what is written here should work elsewhere.
|
||||||
|
|
||||||
```forth
|
```
|
||||||
\ This is a comment
|
\ This is a comment
|
||||||
( This is also a comment but it's only used when defining words )
|
( This is also a comment but it's only used when defining words )
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ Brainfuck (sans majuscule à part au début d’une phrase) est un langage
|
|||||||
Turing-complet extrêmement simple avec seulement 8 commandes.
|
Turing-complet extrêmement simple avec seulement 8 commandes.
|
||||||
|
|
||||||
```
|
```
|
||||||
Tout caractère en dehors de "><+-.,[]" (en dehors des guillements) est ignoré.
|
Tout caractère en dehors de "><+-.,[]" (en dehors des guillemets) est ignoré.
|
||||||
|
|
||||||
Brainfuck est représenté par un tableau de 30 000 cellules initialisées à 0 et
|
Brainfuck est représenté par un tableau de 30 000 cellules initialisées à 0 et
|
||||||
un pointeur de données pointant sur la cellule courante.
|
un pointeur de données pointant sur la cellule courante.
|
||||||
|
@@ -484,7 +484,7 @@ class LearnHaxe3{
|
|||||||
// we can read this variable
|
// we can read this variable
|
||||||
trace(foo_instance.public_read + " is the value for foo_instance.public_read");
|
trace(foo_instance.public_read + " is the value for foo_instance.public_read");
|
||||||
// but not write it
|
// but not write it
|
||||||
// foo_instance.public_write = 4; // this will throw an error if uncommented:
|
// foo_instance.public_read = 4; // this will throw an error if uncommented:
|
||||||
// trace(foo_instance.public_write); // as will this.
|
// trace(foo_instance.public_write); // as will this.
|
||||||
|
|
||||||
trace(foo_instance + " is the value for foo_instance"); // calls the toString method
|
trace(foo_instance + " is the value for foo_instance"); // calls the toString method
|
||||||
|
@@ -83,7 +83,7 @@ True ; => True
|
|||||||
(greet "bilbo") ;=> "hello bilbo"
|
(greet "bilbo") ;=> "hello bilbo"
|
||||||
|
|
||||||
; functions can take optional arguments as well as keyword arguments
|
; functions can take optional arguments as well as keyword arguments
|
||||||
(defn foolist [arg1 &optional [arg2 2]]
|
(defn foolists [arg1 &optional [arg2 2]]
|
||||||
[arg1 arg2])
|
[arg1 arg2])
|
||||||
|
|
||||||
(foolists 3) ;=> [3 2]
|
(foolists 3) ;=> [3 2]
|
||||||
|
@@ -4,6 +4,7 @@ language: java
|
|||||||
contributors:
|
contributors:
|
||||||
- ["Jake Prather", "http://github.com/JakeHP"]
|
- ["Jake Prather", "http://github.com/JakeHP"]
|
||||||
- ["Madison Dickson", "http://github.com/mix3d"]
|
- ["Madison Dickson", "http://github.com/mix3d"]
|
||||||
|
- ["Jakukyo Friel", "http://weakish.github.io"]
|
||||||
filename: LearnJava.java
|
filename: LearnJava.java
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -49,7 +50,7 @@ public class LearnJava {
|
|||||||
// Types & Variables
|
// Types & Variables
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
// Declare a variable using <type> <name> [
|
// Declare a variable using <type> <name>
|
||||||
// Byte - 8-bit signed two's complement integer
|
// Byte - 8-bit signed two's complement integer
|
||||||
// (-128 <= byte <= 127)
|
// (-128 <= byte <= 127)
|
||||||
byte fooByte = 100;
|
byte fooByte = 100;
|
||||||
@@ -268,9 +269,9 @@ public class LearnJava {
|
|||||||
System.out.println(bar); // Prints A, because the statement is true
|
System.out.println(bar); // Prints A, because the statement is true
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// Converting Data Types And Typcasting
|
// Converting Data Types And Typecasting
|
||||||
///////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
// Converting data
|
// Converting data
|
||||||
|
|
||||||
@@ -433,10 +434,12 @@ public interface Digestible {
|
|||||||
|
|
||||||
//We can now create a class that implements both of these interfaces
|
//We can now create a class that implements both of these interfaces
|
||||||
public class Fruit implements Edible, Digestible {
|
public class Fruit implements Edible, Digestible {
|
||||||
|
@Override
|
||||||
public void eat() {
|
public void eat() {
|
||||||
//...
|
//...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void digest() {
|
public void digest() {
|
||||||
//...
|
//...
|
||||||
}
|
}
|
||||||
@@ -445,10 +448,12 @@ public class Fruit implements Edible, Digestible {
|
|||||||
//In java, you can extend only one class, but you can implement many interfaces.
|
//In java, you can extend only one class, but you can implement many interfaces.
|
||||||
//For example:
|
//For example:
|
||||||
public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo {
|
public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo {
|
||||||
|
@Override
|
||||||
public void InterfaceOneMethod() {
|
public void InterfaceOneMethod() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void InterfaceTwoMethod() {
|
public void InterfaceTwoMethod() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -219,8 +219,8 @@ identity 1 # => 1
|
|||||||
|
|
||||||
# Operators are not functions in LiveScript, but you can easily turn
|
# Operators are not functions in LiveScript, but you can easily turn
|
||||||
# them into one! Enter the operator sectioning:
|
# them into one! Enter the operator sectioning:
|
||||||
divide-by-2 = (/ 2)
|
divide-by-two = (/ 2)
|
||||||
[2, 4, 8, 16].map(divide-by-2) .reduce (+)
|
[2, 4, 8, 16].map(divide-by-two) .reduce (+)
|
||||||
|
|
||||||
|
|
||||||
# Not only of function application lives LiveScript, as in any good
|
# Not only of function application lives LiveScript, as in any good
|
||||||
@@ -248,8 +248,8 @@ reduce = (f, xs, initial) --> xs.reduce f, initial
|
|||||||
# The underscore is also used in regular partial application, which you
|
# The underscore is also used in regular partial application, which you
|
||||||
# can use for any function:
|
# can use for any function:
|
||||||
div = (left, right) -> left / right
|
div = (left, right) -> left / right
|
||||||
div-by-2 = div _, 2
|
div-by-two = div _, 2
|
||||||
div-by-2 4 # => 2
|
div-by-two 4 # => 2
|
||||||
|
|
||||||
|
|
||||||
# Last, but not least, LiveScript has back-calls, which might help
|
# Last, but not least, LiveScript has back-calls, which might help
|
||||||
|
@@ -35,7 +35,8 @@ my $variable;
|
|||||||
## * Scalars. They represent a single value. They start with a `$`
|
## * Scalars. They represent a single value. They start with a `$`
|
||||||
|
|
||||||
my $str = 'String';
|
my $str = 'String';
|
||||||
my $str2 = "String"; # double quotes allow for interpolation
|
# double quotes allow for interpolation (which we'll see later):
|
||||||
|
my $str2 = "String";
|
||||||
|
|
||||||
# variable names can contain but not end with simple quotes and dashes,
|
# variable names can contain but not end with simple quotes and dashes,
|
||||||
# and can contain (and end with) underscores :
|
# and can contain (and end with) underscores :
|
||||||
@@ -66,23 +67,13 @@ my @keys = 0, 2;
|
|||||||
@array[@keys] = @letters; # Assign using an array
|
@array[@keys] = @letters; # Assign using an array
|
||||||
say @array; #=> a 6 b
|
say @array; #=> a 6 b
|
||||||
|
|
||||||
# There are two more kinds of lists: Parcel and Arrays.
|
## * Hashes, or key-value Pairs.
|
||||||
# Parcels are immutable lists (you can't modify a list that's not assigned).
|
# Hashes are actually arrays of Pairs
|
||||||
# This is a parcel:
|
# (you can construct a Pair object using the syntax `Key => Value`),
|
||||||
(1, 2, 3); # Not assigned to anything. Changing an element would provoke an error
|
# except they get "flattened" (hash context), removing duplicated keys.
|
||||||
# This is a list:
|
|
||||||
my @a = (1, 2, 3); # Assigned to `@a`. Changing elements is okay!
|
|
||||||
|
|
||||||
# Lists flatten (in list context). You'll see below how to apply item context
|
|
||||||
# or use arrays to have real nested lists.
|
|
||||||
|
|
||||||
|
|
||||||
## * Hashes. Key-Value Pairs.
|
|
||||||
# Hashes are actually arrays of Pairs (`Key => Value`),
|
|
||||||
# except they get "flattened", removing duplicated keys.
|
|
||||||
my %hash = 1 => 2,
|
my %hash = 1 => 2,
|
||||||
3 => 4;
|
3 => 4;
|
||||||
my %hash = autoquoted => "key", # keys *can* get auto-quoted
|
my %hash = autoquoted => "key", # keys get auto-quoted
|
||||||
"some other" => "value", # trailing commas are okay
|
"some other" => "value", # trailing commas are okay
|
||||||
;
|
;
|
||||||
my %hash = <key1 value1 key2 value2>; # you can also create a hash
|
my %hash = <key1 value1 key2 value2>; # you can also create a hash
|
||||||
@@ -112,6 +103,63 @@ sub say-hello-to(Str $name) { # You can provide the type of an argument
|
|||||||
say "Hello, $name !";
|
say "Hello, $name !";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## It can also have optional arguments:
|
||||||
|
sub with-optional($arg?) { # the "?" marks the argument optional
|
||||||
|
say "I might return `(Any)` if I don't have an argument passed,
|
||||||
|
or I'll return my argument";
|
||||||
|
$arg;
|
||||||
|
}
|
||||||
|
with-optional; # returns Any
|
||||||
|
with-optional(); # returns Any
|
||||||
|
with-optional(1); # returns 1
|
||||||
|
|
||||||
|
## You can also give them a default value when they're not passed:
|
||||||
|
sub hello-to($name = "World") {
|
||||||
|
say "Hello, $name !";
|
||||||
|
}
|
||||||
|
hello-to; #=> Hello, World !
|
||||||
|
hello-to(); #=> Hello, World !
|
||||||
|
hello-to('You'); #=> Hello, You !
|
||||||
|
|
||||||
|
## You can also, by using a syntax akin to the one of hashes (yay unified syntax !),
|
||||||
|
## pass *named* arguments to a `sub`.
|
||||||
|
# They're optional, and will default to "Any" (Perl's "null"-like value).
|
||||||
|
sub with-named($normal-arg, :$named) {
|
||||||
|
say $normal-arg + $named;
|
||||||
|
}
|
||||||
|
with-named(1, named => 6); #=> 7
|
||||||
|
# There's one gotcha to be aware of, here:
|
||||||
|
# If you quote your key, Perl 6 won't be able to see it at compile time,
|
||||||
|
# and you'll have a single Pair object as a positional paramater,
|
||||||
|
# which means this fails:
|
||||||
|
with-named(1, 'named' => 6);
|
||||||
|
|
||||||
|
with-named(2, :named(5)); #=> 7
|
||||||
|
|
||||||
|
# To make a named argument mandatory, you can use `?`'s inverse, `!`
|
||||||
|
sub with-mandatory-named(:$str!) {
|
||||||
|
say "$str !";
|
||||||
|
}
|
||||||
|
with-mandatory-named(str => "My String"); #=> My String !
|
||||||
|
with-mandatory-named; # run time error: "Required named parameter not passed"
|
||||||
|
with-mandatory-named(3); # run time error: "Too many positional parameters passed"
|
||||||
|
|
||||||
|
## If a sub takes a named boolean argument ...
|
||||||
|
sub takes-a-bool($name, :$bool) {
|
||||||
|
say "$name takes $bool";
|
||||||
|
}
|
||||||
|
# ... you can use the same "short boolean" hash syntax:
|
||||||
|
takes-a-bool('config', :bool); # config takes True
|
||||||
|
takes-a-bool('config', :!bool); # config takes False
|
||||||
|
|
||||||
|
## You can also provide your named arguments with defaults:
|
||||||
|
sub named-def(:$def = 5) {
|
||||||
|
say $def;
|
||||||
|
}
|
||||||
|
named-def; #=> 5
|
||||||
|
named-def(:10def); #=> 10
|
||||||
|
named-def(def => 15); #=> 15
|
||||||
|
|
||||||
# Since you can omit parenthesis to call a function with no arguments,
|
# Since you can omit parenthesis to call a function with no arguments,
|
||||||
# you need "&" in the name to capture `say-hello`.
|
# you need "&" in the name to capture `say-hello`.
|
||||||
my &s = &say-hello;
|
my &s = &say-hello;
|
||||||
@@ -136,74 +184,6 @@ sub concat3($a, $b, $c) {
|
|||||||
concat3(|@array); #=> a, b, c
|
concat3(|@array); #=> a, b, c
|
||||||
# `@array` got "flattened" as a part of the argument list
|
# `@array` got "flattened" as a part of the argument list
|
||||||
|
|
||||||
## It can also have optional arguments:
|
|
||||||
sub with-optional($arg?) { # the "?" marks the argument optional
|
|
||||||
say "I might return `(Any)` if I don't have an argument passed,
|
|
||||||
or I'll return my argument";
|
|
||||||
$arg;
|
|
||||||
}
|
|
||||||
with-optional; # returns Any
|
|
||||||
with-optional(); # returns Any
|
|
||||||
with-optional(1); # returns 1
|
|
||||||
|
|
||||||
## You can also give them a default value when they're not passed:
|
|
||||||
sub hello-to($name = "World") {
|
|
||||||
say "Hello, $name !";
|
|
||||||
}
|
|
||||||
hello-to; #=> Hello, World !
|
|
||||||
hello-to(); #=> Hello, World !
|
|
||||||
hello-to('You'); #=> Hello, You !
|
|
||||||
|
|
||||||
## You can also, by using a syntax akin to the one of hashes (yay unification !),
|
|
||||||
## pass *named* arguments to a `sub`.
|
|
||||||
sub with-named($normal-arg, :$named) {
|
|
||||||
say $normal-arg + $named;
|
|
||||||
}
|
|
||||||
with-named(1, named => 6); #=> 7
|
|
||||||
# There's one gotcha to be aware of, here:
|
|
||||||
# If you quote your key, Perl 6 won't be able to see it at compile time,
|
|
||||||
# and you'll have a single Pair object as a positional paramater.
|
|
||||||
|
|
||||||
with-named(2, :named(5)); #=> 7
|
|
||||||
with-named(3, :4named); #=> 7
|
|
||||||
# (special colon pair syntax for numbers,
|
|
||||||
# to be used with s// and such, see later)
|
|
||||||
|
|
||||||
with-named(3); # warns, because we tried to use the undefined $named in a `+`:
|
|
||||||
# by default, named arguments are *optional*
|
|
||||||
|
|
||||||
# To make a named argument mandatory, you can use `?`'s inverse, `!`
|
|
||||||
sub with-mandatory-named(:$str!) {
|
|
||||||
say "$str !";
|
|
||||||
}
|
|
||||||
with-mandatory-named(str => "My String"); #=> My String !
|
|
||||||
with-mandatory-named; # run time error: "Required named parameter not passed"
|
|
||||||
with-mandatory-named(3); # run time error: "Too many positional parameters passed"
|
|
||||||
|
|
||||||
## If a sub takes a named boolean argument ...
|
|
||||||
sub takes-a-bool($name, :$bool) {
|
|
||||||
say "$name takes $bool";
|
|
||||||
}
|
|
||||||
# ... you can use the same "short boolean" hash syntax:
|
|
||||||
takes-a-bool('config', :bool); # config takes True
|
|
||||||
takes-a-bool('config', :!bool); # config takes False
|
|
||||||
# or you can use the "adverb" form:
|
|
||||||
takes-a-bool('config'):bool; #=> config takes True
|
|
||||||
takes-a-bool('config'):!bool; #=> config takes False
|
|
||||||
# You'll learn to love (or maybe hate, eh) that syntax later.
|
|
||||||
|
|
||||||
|
|
||||||
## You can also provide your named arguments with defaults:
|
|
||||||
sub named-def(:$def = 5) {
|
|
||||||
say $def;
|
|
||||||
}
|
|
||||||
named-def; #=> 5
|
|
||||||
named-def(:10def); #=> 10
|
|
||||||
named-def(def => 15); #=> 15
|
|
||||||
|
|
||||||
# -- Note: we're going to learn *more* on subs really soon,
|
|
||||||
# but we need to grasp a few more things to understand their real power. Ready?
|
|
||||||
|
|
||||||
### Containers
|
### Containers
|
||||||
# In Perl 6, values are actually stored in "containers".
|
# In Perl 6, values are actually stored in "containers".
|
||||||
# The assignment operator asks the container on the left to store the value on
|
# The assignment operator asks the container on the left to store the value on
|
||||||
@@ -220,23 +200,19 @@ sub mutate($n is rw) {
|
|||||||
|
|
||||||
# A sub itself returns a container, which means it can be marked as rw:
|
# A sub itself returns a container, which means it can be marked as rw:
|
||||||
my $x = 42;
|
my $x = 42;
|
||||||
sub mod() is rw { $x }
|
sub x-store() is rw { $x }
|
||||||
mod() = 52; # in this case, the parentheses are mandatory
|
x-store() = 52; # in this case, the parentheses are mandatory
|
||||||
# (else Perl 6 thinks `mod` is a "term")
|
# (else Perl 6 thinks `mod` is an identifier)
|
||||||
say $x; #=> 52
|
say $x; #=> 52
|
||||||
|
|
||||||
|
|
||||||
### Control Flow Structures
|
### Control Flow Structures
|
||||||
|
|
||||||
# You don't need to put parenthesis around the condition,
|
|
||||||
# but that also means you always have to use brackets (`{ }`) for their body:
|
|
||||||
|
|
||||||
## Conditionals
|
## Conditionals
|
||||||
|
|
||||||
# - `if`
|
# - `if`
|
||||||
# Before talking about `if`, we need to know which values are "Truthy"
|
# Before talking about `if`, we need to know which values are "Truthy"
|
||||||
# (represent True), and which are "Falsey" (or "Falsy") -- represent False.
|
# (represent True), and which are "Falsey" (or "Falsy") -- represent False.
|
||||||
# Only these values are Falsey: (), 0, "0", Nil, A type (like `Str` or `Int`),
|
# Only these values are Falsey: (), 0, "0", "", Nil, A type (like `Str` or `Int`),
|
||||||
# and of course False itself.
|
# and of course False itself.
|
||||||
# Every other value is Truthy.
|
# Every other value is Truthy.
|
||||||
if True {
|
if True {
|
||||||
@@ -247,30 +223,38 @@ unless False {
|
|||||||
say "It's not false !";
|
say "It's not false !";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# As you can see, you don't need parentheses around conditions.
|
||||||
|
# However, you do need the brackets around the "body" block:
|
||||||
|
# if (true) say; # This doesn't work !
|
||||||
|
|
||||||
# You can also use their postfix versions, with the keyword after:
|
# You can also use their postfix versions, with the keyword after:
|
||||||
say "Quite truthy" if True;
|
say "Quite truthy" if True;
|
||||||
|
|
||||||
# if (true) say; # This doesn't work !
|
|
||||||
|
|
||||||
# - Ternary conditional, "?? !!" (like `x ? y : z` in some other languages)
|
# - Ternary conditional, "?? !!" (like `x ? y : z` in some other languages)
|
||||||
my $a = $condition ?? $value-if-true !! $value-if-false;
|
my $a = $condition ?? $value-if-true !! $value-if-false;
|
||||||
|
|
||||||
# - `given`-`when` looks like other languages `switch`, but much more
|
# - `given`-`when` looks like other languages `switch`, but much more
|
||||||
# powerful thanks to smart matching and thanks to Perl 6's "topic variable", $_.
|
# powerful thanks to smart matching and thanks to Perl 6's "topic variable", $_.
|
||||||
|
#
|
||||||
# This variable contains the default argument of a block,
|
# This variable contains the default argument of a block,
|
||||||
# a loop's current iteration (unless explicitly named), etc.
|
# a loop's current iteration (unless explicitly named), etc.
|
||||||
|
#
|
||||||
# `given` simply puts its argument into `$_` (like a block would do),
|
# `given` simply puts its argument into `$_` (like a block would do),
|
||||||
# and `when` compares it using the "smart matching" (`~~`) operator.
|
# and `when` compares it using the "smart matching" (`~~`) operator.
|
||||||
|
#
|
||||||
# Since other Perl 6 constructs use this variable (as said before, like `for`,
|
# Since other Perl 6 constructs use this variable (as said before, like `for`,
|
||||||
# blocks, etc), this means the powerful `when` is not only applicable along with
|
# blocks, etc), this means the powerful `when` is not only applicable along with
|
||||||
# a `given`, but instead anywhere a `$_` exists.
|
# a `given`, but instead anywhere a `$_` exists.
|
||||||
given "foo bar" {
|
given "foo bar" {
|
||||||
when /foo/ { # Don't worry about smart matching -- just know `when` uses it.
|
say $_; #=> foo bar
|
||||||
|
when /foo/ { # Don't worry about smart matching yet – just know `when` uses it.
|
||||||
# This is equivalent to `if $_ ~~ /foo/`.
|
# This is equivalent to `if $_ ~~ /foo/`.
|
||||||
say "Yay !";
|
say "Yay !";
|
||||||
}
|
}
|
||||||
when $_.chars > 50 { # smart matching anything with True (`$a ~~ True`) is True,
|
when $_.chars > 50 { # smart matching anything with True (`$a ~~ True`) is True,
|
||||||
# so you can also put "normal" conditionals.
|
# so you can also put "normal" conditionals.
|
||||||
|
# This when is equivalent to this `if`:
|
||||||
|
# if ($_.chars > 50) ~~ True {...}
|
||||||
say "Quite a long string !";
|
say "Quite a long string !";
|
||||||
}
|
}
|
||||||
default { # same as `when *` (using the Whatever Star)
|
default { # same as `when *` (using the Whatever Star)
|
||||||
@@ -281,7 +265,7 @@ given "foo bar" {
|
|||||||
## Looping constructs
|
## Looping constructs
|
||||||
|
|
||||||
# - `loop` is an infinite loop if you don't pass it arguments,
|
# - `loop` is an infinite loop if you don't pass it arguments,
|
||||||
# but can also be a c-style `for`:
|
# but can also be a C-style `for` loop:
|
||||||
loop {
|
loop {
|
||||||
say "This is an infinite loop !";
|
say "This is an infinite loop !";
|
||||||
last; # last breaks out of the loop, like the `break` keyword in other languages
|
last; # last breaks out of the loop, like the `break` keyword in other languages
|
||||||
@@ -296,7 +280,7 @@ loop (my $i = 0; $i < 5; $i++) {
|
|||||||
|
|
||||||
# - `for` - Passes through an array
|
# - `for` - Passes through an array
|
||||||
for @array -> $variable {
|
for @array -> $variable {
|
||||||
say "I've found $variable !";
|
say "I've got $variable !";
|
||||||
}
|
}
|
||||||
|
|
||||||
# As we saw with given, for's default "current iteration" variable is `$_`.
|
# As we saw with given, for's default "current iteration" variable is `$_`.
|
||||||
@@ -316,22 +300,15 @@ for @array {
|
|||||||
last if $_ == 5; # Or break out of a loop (like `break` in C-like languages).
|
last if $_ == 5; # Or break out of a loop (like `break` in C-like languages).
|
||||||
}
|
}
|
||||||
|
|
||||||
# Note - the "lambda" `->` syntax isn't reserved to `for`:
|
# The "pointy block" syntax isn't specific to for.
|
||||||
|
# It's just a way to express a block in Perl6.
|
||||||
if long-computation() -> $result {
|
if long-computation() -> $result {
|
||||||
say "The result is $result";
|
say "The result is $result";
|
||||||
}
|
}
|
||||||
|
|
||||||
## Loops can also have a label, and be jumped to through these.
|
|
||||||
OUTER: while 1 {
|
|
||||||
say "hey";
|
|
||||||
while 1 {
|
|
||||||
OUTER.last; # All the control keywords must be called on the label itself
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now that you've seen how to traverse a list, you need to be aware of something:
|
# Now that you've seen how to traverse a list, you need to be aware of something:
|
||||||
# List context (@) flattens. If you traverse nested lists, you'll actually be traversing a
|
# List context (@) flattens. If you traverse nested lists, you'll actually be traversing a
|
||||||
# shallow list (except if some sub-list were put in item context ($)).
|
# shallow list.
|
||||||
for 1, 2, (3, (4, ((5)))) {
|
for 1, 2, (3, (4, ((5)))) {
|
||||||
say "Got $_.";
|
say "Got $_.";
|
||||||
} #=> Got 1. Got 2. Got 3. Got 4. Got 5.
|
} #=> Got 1. Got 2. Got 3. Got 4. Got 5.
|
||||||
@@ -348,9 +325,14 @@ for [1, 2, 3, 4] {
|
|||||||
say "Got $_.";
|
say "Got $_.";
|
||||||
} #=> Got 1 2 3 4.
|
} #=> Got 1 2 3 4.
|
||||||
|
|
||||||
# The other difference between `$()` and `[]` is that `[]` always returns a mutable Array
|
# You need to be aware of when flattening happens exactly.
|
||||||
# whereas `$()` will return a Parcel when given a Parcel.
|
# The general guideline is that argument lists flatten, but not method calls.
|
||||||
|
# Also note that `.list` and array assignment flatten (`@ary = ...`) flatten.
|
||||||
|
((1,2), 3, (4,5)).map({...}); # iterates over three elements (method call)
|
||||||
|
map {...}, ((1,2),3,(4,5)); # iterates over five elements (argument list is flattened)
|
||||||
|
|
||||||
|
(@a, @b, @c).pick(1); # picks one of three arrays (method call)
|
||||||
|
pick 1, @a, @b, @c; # flattens argument list and pick one element
|
||||||
|
|
||||||
### Operators
|
### Operators
|
||||||
|
|
||||||
@@ -394,9 +376,6 @@ $arg ~~ &bool-returning-function; # `True` if the function, passed `$arg`
|
|||||||
1 ~~ True; # smart-matching against a boolean always returns that boolean
|
1 ~~ True; # smart-matching against a boolean always returns that boolean
|
||||||
# (and will warn).
|
# (and will warn).
|
||||||
|
|
||||||
# - `===` is value identity and uses `.WHICH` on the objects to compare them
|
|
||||||
# - `=:=` is container identity and uses `VAR()` on the objects to compare them
|
|
||||||
|
|
||||||
# You also, of course, have `<`, `<=`, `>`, `>=`.
|
# You also, of course, have `<`, `<=`, `>`, `>=`.
|
||||||
# Their string equivalent are also avaiable : `lt`, `le`, `gt`, `ge`.
|
# Their string equivalent are also avaiable : `lt`, `le`, `gt`, `ge`.
|
||||||
3 > 4;
|
3 > 4;
|
||||||
@@ -559,6 +538,21 @@ map(sub ($a, $b) { $a + $b + 3 }, @array); # (here with `sub`)
|
|||||||
# Note : those are sorted lexicographically.
|
# Note : those are sorted lexicographically.
|
||||||
# `{ $^b / $^a }` is like `-> $a, $b { $b / $a }`
|
# `{ $^b / $^a }` is like `-> $a, $b { $b / $a }`
|
||||||
|
|
||||||
|
## About types...
|
||||||
|
# Perl6 is gradually typed. This means you can specify the type
|
||||||
|
# of your variables/arguments/return types, or you can omit them
|
||||||
|
# and they'll default to "Any".
|
||||||
|
# You obviously get access to a few base types, like Int and Str.
|
||||||
|
# The constructs for declaring types are "class", "role",
|
||||||
|
# which you'll see later.
|
||||||
|
|
||||||
|
# For now, let us examinate "subset":
|
||||||
|
# a "subset" is a "sub-type" with additional checks.
|
||||||
|
# For example: "a very big integer is an Int that's greater than 500"
|
||||||
|
# You can specify the type you're subtyping (by default, Any),
|
||||||
|
# and add additional checks with the "where" keyword:
|
||||||
|
subset VeryBigInteger of Int where * > 500;
|
||||||
|
|
||||||
## Multiple Dispatch
|
## Multiple Dispatch
|
||||||
# Perl 6 can decide which variant of a `sub` to call based on the type of the
|
# Perl 6 can decide which variant of a `sub` to call based on the type of the
|
||||||
# arguments, or on arbitrary preconditions, like with a type or a `where`:
|
# arguments, or on arbitrary preconditions, like with a type or a `where`:
|
||||||
@@ -567,20 +561,19 @@ map(sub ($a, $b) { $a + $b + 3 }, @array); # (here with `sub`)
|
|||||||
multi sub sayit(Int $n) { # note the `multi` keyword here
|
multi sub sayit(Int $n) { # note the `multi` keyword here
|
||||||
say "Number: $n";
|
say "Number: $n";
|
||||||
}
|
}
|
||||||
multi sayit(Str $s) } # the `sub` is the default
|
multi sayit(Str $s) } # a multi is a `sub` by default
|
||||||
say "String: $s";
|
say "String: $s";
|
||||||
}
|
}
|
||||||
sayit("foo"); # prints "String: foo"
|
sayit("foo"); # prints "String: foo"
|
||||||
sayit(True); # fails at *compile time* with
|
sayit(True); # fails at *compile time* with
|
||||||
# "calling 'sayit' will never work with arguments of types ..."
|
# "calling 'sayit' will never work with arguments of types ..."
|
||||||
|
|
||||||
# with arbitrary precondition:
|
# with arbitrary precondition (remember subsets?):
|
||||||
multi is-big(Int $n where * > 50) { "Yes !" } # using a closure
|
multi is-big(Int $n where * > 50) { "Yes !" } # using a closure
|
||||||
multi is-big(Int $ where 10..50) { "Quite." } # Using smart-matching
|
multi is-big(Int $ where 10..50) { "Quite." } # Using smart-matching
|
||||||
# (could use a regexp, etc)
|
# (could use a regexp, etc)
|
||||||
multi is-big(Int $) { "No" }
|
multi is-big(Int $) { "No" }
|
||||||
|
|
||||||
# You can also name these checks, by creating "subsets":
|
|
||||||
subset Even of Int where * %% 2;
|
subset Even of Int where * %% 2;
|
||||||
|
|
||||||
multi odd-or-even(Even) { "Even" } # The main case using the type.
|
multi odd-or-even(Even) { "Even" } # The main case using the type.
|
||||||
@@ -724,7 +717,7 @@ role PrintableVal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# you "use" a mixin with "does" :
|
# you "import" a mixin (a "role") with "does":
|
||||||
class Item does PrintableVal {
|
class Item does PrintableVal {
|
||||||
has $.val;
|
has $.val;
|
||||||
|
|
||||||
@@ -1083,9 +1076,7 @@ postcircumfix:<{ }>(%h, $key, :delete); # (you can call operators like that)
|
|||||||
# It's a prefix meta-operator that takes a binary functions and
|
# It's a prefix meta-operator that takes a binary functions and
|
||||||
# one or many lists. If it doesn't get passed any argument,
|
# one or many lists. If it doesn't get passed any argument,
|
||||||
# it either return a "default value" for this operator
|
# it either return a "default value" for this operator
|
||||||
# (a value that wouldn't change the result if passed as one
|
# (a meaningless value) or `Any` if there's none (examples below).
|
||||||
# of the element of the list to be passed to the operator),
|
|
||||||
# or `Any` if there's none (examples below).
|
|
||||||
#
|
#
|
||||||
# Otherwise, it pops an element from the list(s) one at a time, and applies
|
# Otherwise, it pops an element from the list(s) one at a time, and applies
|
||||||
# the binary function to the last result (or the list's first element)
|
# the binary function to the last result (or the list's first element)
|
||||||
@@ -1107,9 +1098,7 @@ say [//] Nil, Any, False, 1, 5; #=> False
|
|||||||
# Default value examples:
|
# Default value examples:
|
||||||
say [*] (); #=> 1
|
say [*] (); #=> 1
|
||||||
say [+] (); #=> 0
|
say [+] (); #=> 0
|
||||||
# In both cases, they're results that, were they in the lists,
|
# meaningless values, since N*1=N and N+0=N.
|
||||||
# wouldn't have any impact on the final value
|
|
||||||
# (since N*1=N and N+0=N).
|
|
||||||
say [//]; #=> (Any)
|
say [//]; #=> (Any)
|
||||||
# There's no "default value" for `//`.
|
# There's no "default value" for `//`.
|
||||||
|
|
||||||
@@ -1163,90 +1152,6 @@ say @fib[^10]; #=> 1 1 2 3 5 8 13 21 34 55
|
|||||||
# That's why `@primes[^100]` will take a long time the first time you print
|
# That's why `@primes[^100]` will take a long time the first time you print
|
||||||
# it, then be instant.
|
# it, then be instant.
|
||||||
|
|
||||||
|
|
||||||
## * Sort comparison
|
|
||||||
# They return one value of the `Order` enum : `Less`, `Same` and `More`
|
|
||||||
# (which numerify to -1, 0 or +1).
|
|
||||||
1 <=> 4; # sort comparison for numerics
|
|
||||||
'a' leg 'b'; # sort comparison for string
|
|
||||||
$obj eqv $obj2; # sort comparison using eqv semantics
|
|
||||||
|
|
||||||
## * Generic ordering
|
|
||||||
3 before 4; # True
|
|
||||||
'b' after 'a'; # True
|
|
||||||
|
|
||||||
## * Short-circuit default operator
|
|
||||||
# Like `or` and `||`, but instead returns the first *defined* value :
|
|
||||||
say Any // Nil // 0 // 5; #=> 0
|
|
||||||
|
|
||||||
## * Short-circuit exclusive or (XOR)
|
|
||||||
# Returns `True` if one (and only one) of its arguments is true
|
|
||||||
say True ^^ False; #=> True
|
|
||||||
|
|
||||||
## * Flip Flop
|
|
||||||
# The flip flop operators (`ff` and `fff`, equivalent to P5's `..`/`...`).
|
|
||||||
# are operators that take two predicates to test:
|
|
||||||
# They are `False` until their left side returns `True`, then are `True` until
|
|
||||||
# their right side returns `True`.
|
|
||||||
# Like for ranges, you can exclude the iteration when it became `True`/`False`
|
|
||||||
# by using `^` on either side.
|
|
||||||
# Let's start with an example :
|
|
||||||
for <well met young hero we shall meet later> {
|
|
||||||
# by default, `ff`/`fff` smart-match (`~~`) against `$_`:
|
|
||||||
if 'met' ^ff 'meet' { # Won't enter the if for "met"
|
|
||||||
# (explained in details below).
|
|
||||||
.say
|
|
||||||
}
|
|
||||||
|
|
||||||
if rand == 0 ff rand == 1 { # compare variables other than `$_`
|
|
||||||
say "This ... probably will never run ...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# This will print "young hero we shall meet" (excluding "met"):
|
|
||||||
# the flip-flop will start returning `True` when it first encounters "met"
|
|
||||||
# (but will still return `False` for "met" itself, due to the leading `^`
|
|
||||||
# on `ff`), until it sees "meet", which is when it'll start returning `False`.
|
|
||||||
|
|
||||||
# The difference between `ff` (awk-style) and `fff` (sed-style) is that
|
|
||||||
# `ff` will test its right side right when its left side changes to `True`,
|
|
||||||
# and can get back to `False` right away
|
|
||||||
# (*except* it'll be `True` for the iteration that matched) -
|
|
||||||
# While `fff` will wait for the next iteration to
|
|
||||||
# try its right side, once its left side changed:
|
|
||||||
.say if 'B' ff 'B' for <A B C B A>; #=> B B
|
|
||||||
# because the right-hand-side was tested
|
|
||||||
# directly (and returned `True`).
|
|
||||||
# "B"s are printed since it matched that time
|
|
||||||
# (it just went back to `False` right away).
|
|
||||||
.say if 'B' fff 'B' for <A B C B A>; #=> B C B
|
|
||||||
# The right-hand-side wasn't tested until
|
|
||||||
# `$_` became "C"
|
|
||||||
# (and thus did not match instantly).
|
|
||||||
|
|
||||||
# A flip-flop can change state as many times as needed:
|
|
||||||
for <test start print it stop not printing start print again stop not anymore> {
|
|
||||||
.say if $_ eq 'start' ^ff^ $_ eq 'stop'; # exclude both "start" and "stop",
|
|
||||||
#=> "print this printing again"
|
|
||||||
}
|
|
||||||
|
|
||||||
# you might also use a Whatever Star,
|
|
||||||
# which is equivalent to `True` for the left side or `False` for the right:
|
|
||||||
for (1, 3, 60, 3, 40, 60) { # Note: the parenthesis are superfluous here
|
|
||||||
# (sometimes called "superstitious parentheses")
|
|
||||||
.say if $_ > 50 ff *; # Once the flip-flop reaches a number greater than 50,
|
|
||||||
# it'll never go back to `False`
|
|
||||||
#=> 60 3 40 60
|
|
||||||
}
|
|
||||||
|
|
||||||
# You can also use this property to create an `If`
|
|
||||||
# that'll not go through the first time :
|
|
||||||
for <a b c> {
|
|
||||||
.say if * ^ff *; # the flip-flop is `True` and never goes back to `False`,
|
|
||||||
# but the `^` makes it *not run* on the first iteration
|
|
||||||
#=> b c
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Regular Expressions
|
### Regular Expressions
|
||||||
# I'm sure a lot of you have been waiting for this one.
|
# I'm sure a lot of you have been waiting for this one.
|
||||||
# Well, now that you know a good deal of Perl 6 already, we can get started.
|
# Well, now that you know a good deal of Perl 6 already, we can get started.
|
||||||
@@ -1470,6 +1375,105 @@ multi MAIN('import', File, Str :$as) { ... } # omitting parameter name
|
|||||||
# As you can see, this is *very* powerful.
|
# As you can see, this is *very* powerful.
|
||||||
# It even went as far as to show inline the constants.
|
# It even went as far as to show inline the constants.
|
||||||
# (the type is only displayed if the argument is `$`/is named)
|
# (the type is only displayed if the argument is `$`/is named)
|
||||||
|
|
||||||
|
###
|
||||||
|
### APPENDIX A:
|
||||||
|
###
|
||||||
|
### List of things
|
||||||
|
###
|
||||||
|
|
||||||
|
# It's considered by now you know the Perl6 basics.
|
||||||
|
# This section is just here to list some common operations,
|
||||||
|
# but which are not in the "main part" of the tutorial to bloat it up
|
||||||
|
|
||||||
|
## Operators
|
||||||
|
|
||||||
|
|
||||||
|
## * Sort comparison
|
||||||
|
# They return one value of the `Order` enum : `Less`, `Same` and `More`
|
||||||
|
# (which numerify to -1, 0 or +1).
|
||||||
|
1 <=> 4; # sort comparison for numerics
|
||||||
|
'a' leg 'b'; # sort comparison for string
|
||||||
|
$obj eqv $obj2; # sort comparison using eqv semantics
|
||||||
|
|
||||||
|
## * Generic ordering
|
||||||
|
3 before 4; # True
|
||||||
|
'b' after 'a'; # True
|
||||||
|
|
||||||
|
## * Short-circuit default operator
|
||||||
|
# Like `or` and `||`, but instead returns the first *defined* value :
|
||||||
|
say Any // Nil // 0 // 5; #=> 0
|
||||||
|
|
||||||
|
## * Short-circuit exclusive or (XOR)
|
||||||
|
# Returns `True` if one (and only one) of its arguments is true
|
||||||
|
say True ^^ False; #=> True
|
||||||
|
## * Flip Flop
|
||||||
|
# The flip flop operators (`ff` and `fff`, equivalent to P5's `..`/`...`).
|
||||||
|
# are operators that take two predicates to test:
|
||||||
|
# They are `False` until their left side returns `True`, then are `True` until
|
||||||
|
# their right side returns `True`.
|
||||||
|
# Like for ranges, you can exclude the iteration when it became `True`/`False`
|
||||||
|
# by using `^` on either side.
|
||||||
|
# Let's start with an example :
|
||||||
|
for <well met young hero we shall meet later> {
|
||||||
|
# by default, `ff`/`fff` smart-match (`~~`) against `$_`:
|
||||||
|
if 'met' ^ff 'meet' { # Won't enter the if for "met"
|
||||||
|
# (explained in details below).
|
||||||
|
.say
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand == 0 ff rand == 1 { # compare variables other than `$_`
|
||||||
|
say "This ... probably will never run ...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# This will print "young hero we shall meet" (excluding "met"):
|
||||||
|
# the flip-flop will start returning `True` when it first encounters "met"
|
||||||
|
# (but will still return `False` for "met" itself, due to the leading `^`
|
||||||
|
# on `ff`), until it sees "meet", which is when it'll start returning `False`.
|
||||||
|
|
||||||
|
# The difference between `ff` (awk-style) and `fff` (sed-style) is that
|
||||||
|
# `ff` will test its right side right when its left side changes to `True`,
|
||||||
|
# and can get back to `False` right away
|
||||||
|
# (*except* it'll be `True` for the iteration that matched) -
|
||||||
|
# While `fff` will wait for the next iteration to
|
||||||
|
# try its right side, once its left side changed:
|
||||||
|
.say if 'B' ff 'B' for <A B C B A>; #=> B B
|
||||||
|
# because the right-hand-side was tested
|
||||||
|
# directly (and returned `True`).
|
||||||
|
# "B"s are printed since it matched that time
|
||||||
|
# (it just went back to `False` right away).
|
||||||
|
.say if 'B' fff 'B' for <A B C B A>; #=> B C B
|
||||||
|
# The right-hand-side wasn't tested until
|
||||||
|
# `$_` became "C"
|
||||||
|
# (and thus did not match instantly).
|
||||||
|
|
||||||
|
# A flip-flop can change state as many times as needed:
|
||||||
|
for <test start print it stop not printing start print again stop not anymore> {
|
||||||
|
.say if $_ eq 'start' ^ff^ $_ eq 'stop'; # exclude both "start" and "stop",
|
||||||
|
#=> "print this printing again"
|
||||||
|
}
|
||||||
|
|
||||||
|
# you might also use a Whatever Star,
|
||||||
|
# which is equivalent to `True` for the left side or `False` for the right:
|
||||||
|
for (1, 3, 60, 3, 40, 60) { # Note: the parenthesis are superfluous here
|
||||||
|
# (sometimes called "superstitious parentheses")
|
||||||
|
.say if $_ > 50 ff *; # Once the flip-flop reaches a number greater than 50,
|
||||||
|
# it'll never go back to `False`
|
||||||
|
#=> 60 3 40 60
|
||||||
|
}
|
||||||
|
|
||||||
|
# You can also use this property to create an `If`
|
||||||
|
# that'll not go through the first time :
|
||||||
|
for <a b c> {
|
||||||
|
.say if * ^ff *; # the flip-flop is `True` and never goes back to `False`,
|
||||||
|
# but the `^` makes it *not run* on the first iteration
|
||||||
|
#=> b c
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# - `===` is value identity and uses `.WHICH` on the objects to compare them
|
||||||
|
# - `=:=` is container identity and uses `VAR()` on the objects to compare them
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to go further, you can:
|
If you want to go further, you can:
|
||||||
@@ -1477,5 +1481,5 @@ If you want to go further, you can:
|
|||||||
- Read the [Perl 6 Advent Calendar](http://perl6advent.wordpress.com/). This is probably the greatest source of Perl 6 information, snippets and such.
|
- Read the [Perl 6 Advent Calendar](http://perl6advent.wordpress.com/). This is probably the greatest source of Perl 6 information, snippets and such.
|
||||||
- Come along on `#perl6` at `irc.freenode.net`. The folks here are always helpful.
|
- Come along on `#perl6` at `irc.freenode.net`. The folks here are always helpful.
|
||||||
- Check the [source of Perl 6's functions and classes](https://github.com/rakudo/rakudo/tree/nom/src/core). Rakudo is mainly written in Perl 6 (with a lot of NQP, "Not Quite Perl", a Perl 6 subset easier to implement and optimize).
|
- Check the [source of Perl 6's functions and classes](https://github.com/rakudo/rakudo/tree/nom/src/core). Rakudo is mainly written in Perl 6 (with a lot of NQP, "Not Quite Perl", a Perl 6 subset easier to implement and optimize).
|
||||||
- Read the [Synopses](perlcabal.org/syn). They explain it from an implementor point-of-view, but it's still very interesting.
|
- Read [the language design documents](http://design.perl6.org). They explain P6 from an implementor point-of-view, but it's still very interesting.
|
||||||
|
|
||||||
|
@@ -199,4 +199,4 @@ That's it.
|
|||||||
|
|
||||||
Download [Node.js](http://nodejs.org/) and `npm install pogo`.
|
Download [Node.js](http://nodejs.org/) and `npm install pogo`.
|
||||||
|
|
||||||
There is plenty of documentation on [http://pogoscript.org/](http://pogoscript.org/), inlcuding a [cheat sheet](http://pogoscript.org/cheatsheet.html), a [guide](http://pogoscript.org/guide/), and how [Pogoscript translates to Javascript](http://featurist.github.io/pogo-examples/). Get in touch on the [google group](http://groups.google.com/group/pogoscript) if you have questions!
|
There is plenty of documentation on [http://pogoscript.org/](http://pogoscript.org/), including a [cheat sheet](http://pogoscript.org/cheatsheet.html), a [guide](http://pogoscript.org/guide/), and how [Pogoscript translates to Javascript](http://featurist.github.io/pogo-examples/). Get in touch on the [google group](http://groups.google.com/group/pogoscript) if you have questions!
|
||||||
|
384
pt-br/clojure-pt.html.markdown
Normal file
384
pt-br/clojure-pt.html.markdown
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
---
|
||||||
|
language: clojure
|
||||||
|
filename: learnclojure-pt.clj
|
||||||
|
contributors:
|
||||||
|
- ["Adam Bard", "http://adambard.com/"]
|
||||||
|
translators:
|
||||||
|
- ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"]
|
||||||
|
lang: pt-br
|
||||||
|
---
|
||||||
|
|
||||||
|
Clojure é uma linguagem da família do Lisp desenvolvida para a JVM (máquina virtual Java). Possui uma ênfase muito mais forte em [programação funcional] (https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional) pura do que Common Lisp, mas inclui diversas utilidades [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) para lidar com estado a medida que isso se torna necessário.
|
||||||
|
|
||||||
|
Essa combinação permite gerenciar processamento concorrente de maneira muito simples, e frequentemente de maneira automática.
|
||||||
|
|
||||||
|
(Sua versão de clojure precisa ser pelo menos 1.2)
|
||||||
|
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
; Comentários começam por ponto e vírgula
|
||||||
|
|
||||||
|
; Clojure é escrito em "forms", os quais são simplesmente
|
||||||
|
; listas de coisas dentro de parênteses, separados por espaços em branco.
|
||||||
|
|
||||||
|
; O "reader" (leitor) de Clojure presume que o primeiro elemento de
|
||||||
|
; uma par de parênteses é uma função ou macro, e que os resto são argumentos.
|
||||||
|
|
||||||
|
: A primeira chamada de um arquivo deve ser ns, para configurar o namespace (espaço de nomes)
|
||||||
|
(ns learnclojure)
|
||||||
|
|
||||||
|
; Alguns exemplos básicos:
|
||||||
|
|
||||||
|
; str cria uma string concatenando seus argumentos
|
||||||
|
(str "Hello" " " "World") ; => "Hello World"
|
||||||
|
|
||||||
|
; Cálculos são feitos de forma direta e intuitiva
|
||||||
|
(+ 1 1) ; => 2
|
||||||
|
(- 2 1) ; => 1
|
||||||
|
(* 1 2) ; => 2
|
||||||
|
(/ 2 1) ; => 2
|
||||||
|
|
||||||
|
; Você pode comparar igualdade utilizando =
|
||||||
|
(= 1 1) ; => true
|
||||||
|
(= 2 1) ; => false
|
||||||
|
|
||||||
|
; Negação para operações lógicas
|
||||||
|
(not true) ; => false
|
||||||
|
|
||||||
|
; Aninhar "forms" funciona como esperado
|
||||||
|
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
|
||||||
|
|
||||||
|
; Tipos
|
||||||
|
;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Clojure usa os tipos de objetos de Java para booleanos, strings e números.
|
||||||
|
; Use `class` para inspecioná-los
|
||||||
|
(class 1) ; Literais Integer são java.lang.Long por padrão
|
||||||
|
(class 1.); Literais Float são java.lang.Double
|
||||||
|
(class ""); Strings são sempre com aspas duplas, e são java.lang.String
|
||||||
|
(class false) ; Booleanos são java.lang.Boolean
|
||||||
|
(class nil); O valor "null" é chamado nil
|
||||||
|
|
||||||
|
; Se você quiser criar um lista de literais, use aspa simples para
|
||||||
|
; ela não ser avaliada
|
||||||
|
'(+ 1 2) ; => (+ 1 2)
|
||||||
|
; (que é uma abreviação de (quote (+ 1 2)))
|
||||||
|
|
||||||
|
; É possível avaliar uma lista com aspa simples
|
||||||
|
(eval '(+ 1 2)) ; => 3
|
||||||
|
|
||||||
|
; Coleções e sequências
|
||||||
|
;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Listas são estruturas encadeadas, enquanto vetores são implementados como arrays.
|
||||||
|
; Listas e Vetores são classes Java também!
|
||||||
|
(class [1 2 3]); => clojure.lang.PersistentVector
|
||||||
|
(class '(1 2 3)); => clojure.lang.PersistentList
|
||||||
|
|
||||||
|
; Uma lista é escrita como (1 2 3), mas temos que colocar a aspa
|
||||||
|
; simples para impedir o leitor (reader) de pensar que é uma função.
|
||||||
|
; Também, (list 1 2 3) é o mesmo que '(1 2 3)
|
||||||
|
|
||||||
|
; "Coleções" são apenas grupos de dados
|
||||||
|
; Listas e vetores são ambos coleções:
|
||||||
|
(coll? '(1 2 3)) ; => true
|
||||||
|
(coll? [1 2 3]) ; => true
|
||||||
|
|
||||||
|
; "Sequências" (seqs) são descrições abstratas de listas de dados.
|
||||||
|
; Apenas listas são seqs.
|
||||||
|
(seq? '(1 2 3)) ; => true
|
||||||
|
(seq? [1 2 3]) ; => false
|
||||||
|
|
||||||
|
; Um seq precisa apenas prover uma entrada quando é acessada.
|
||||||
|
; Portanto, já que seqs podem ser avaliadas sob demanda (lazy) -- elas podem definir séries infinitas:
|
||||||
|
(range 4) ; => (0 1 2 3)
|
||||||
|
(range) ; => (0 1 2 3 4 ...) (uma série infinita)
|
||||||
|
(take 4 (range)) ; (0 1 2 3)
|
||||||
|
|
||||||
|
; Use cons para adicionar um item no início de uma lista ou vetor
|
||||||
|
(cons 4 [1 2 3]) ; => (4 1 2 3)
|
||||||
|
(cons 4 '(1 2 3)) ; => (4 1 2 3)
|
||||||
|
|
||||||
|
; Conj adiciona um item em uma coleção sempre do jeito mais eficiente.
|
||||||
|
; Para listas, elas inserem no início. Para vetores, é inserido no final.
|
||||||
|
(conj [1 2 3] 4) ; => [1 2 3 4]
|
||||||
|
(conj '(1 2 3) 4) ; => (4 1 2 3)
|
||||||
|
|
||||||
|
; Use concat para concatenar listas e vetores
|
||||||
|
(concat [1 2] '(3 4)) ; => (1 2 3 4)
|
||||||
|
|
||||||
|
; Use filter, map para interagir com coleções
|
||||||
|
(map inc [1 2 3]) ; => (2 3 4)
|
||||||
|
(filter even? [1 2 3]) ; => (2)
|
||||||
|
|
||||||
|
; Use reduce para reduzi-los
|
||||||
|
(reduce + [1 2 3 4])
|
||||||
|
; = (+ (+ (+ 1 2) 3) 4)
|
||||||
|
; => 10
|
||||||
|
|
||||||
|
; Reduce pode receber um argumento para o valor inicial
|
||||||
|
(reduce conj [] '(3 2 1))
|
||||||
|
; = (conj (conj (conj [] 3) 2) 1)
|
||||||
|
; => [3 2 1]
|
||||||
|
|
||||||
|
; Funções
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Use fn para criar novas funções. Uma função sempre retorna
|
||||||
|
; sua última expressão.
|
||||||
|
(fn [] "Hello World") ; => fn
|
||||||
|
|
||||||
|
; (É necessário colocar parênteses para chamá-los)
|
||||||
|
((fn [] "Hello World")) ; => "Hello World"
|
||||||
|
|
||||||
|
; Você pode atribuir valores a variáveis utilizando def
|
||||||
|
(def x 1)
|
||||||
|
x ; => 1
|
||||||
|
|
||||||
|
; Atribua uma função para uma var
|
||||||
|
(def hello-world (fn [] "Hello World"))
|
||||||
|
(hello-world) ; => "Hello World"
|
||||||
|
|
||||||
|
; Você pode abreviar esse processo usando defn
|
||||||
|
(defn hello-world [] "Hello World")
|
||||||
|
|
||||||
|
; O [] é uma lista de argumentos para um função.
|
||||||
|
(defn hello [name]
|
||||||
|
(str "Hello " name))
|
||||||
|
(hello "Steve") ; => "Hello Steve"
|
||||||
|
|
||||||
|
; Você pode ainda usar essa abreviação para criar funcões:
|
||||||
|
(def hello2 #(str "Hello " %1))
|
||||||
|
(hello2 "Fanny") ; => "Hello Fanny"
|
||||||
|
|
||||||
|
; Vocé pode ter funções multi-variadic, isto é, com um número variável de argumentos
|
||||||
|
(defn hello3
|
||||||
|
([] "Hello World")
|
||||||
|
([name] (str "Hello " name)))
|
||||||
|
(hello3 "Jake") ; => "Hello Jake"
|
||||||
|
(hello3) ; => "Hello World"
|
||||||
|
|
||||||
|
; Funções podem agrupar argumentos extras em uma seq
|
||||||
|
(defn count-args [& args]
|
||||||
|
(str "You passed " (count args) " args: " args))
|
||||||
|
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
|
||||||
|
|
||||||
|
; Você pode misturar argumentos regulares e argumentos em seq
|
||||||
|
(defn hello-count [name & args]
|
||||||
|
(str "Hello " name ", you passed " (count args) " extra args"))
|
||||||
|
(hello-count "Finn" 1 2 3)
|
||||||
|
; => "Hello Finn, you passed 3 extra args"
|
||||||
|
|
||||||
|
|
||||||
|
; Mapas
|
||||||
|
;;;;;;;;;;
|
||||||
|
|
||||||
|
; Hash maps e array maps compartilham uma mesma interface. Hash maps são mais
|
||||||
|
; rápidos para pesquisa mas não mantém a ordem da chave.
|
||||||
|
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
|
||||||
|
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
|
||||||
|
|
||||||
|
; Arraymaps pode automaticamente se tornar hashmaps através da maioria das
|
||||||
|
; operações se eles ficarem grandes o suficiente, portanto não há necessida de
|
||||||
|
; se preocupar com isso.
|
||||||
|
|
||||||
|
;Mapas podem usar qualquer valor que se pode derivar um hash como chave
|
||||||
|
|
||||||
|
|
||||||
|
; Mapas podem usar qualquer valor em que se pode derivar um hash como chave,
|
||||||
|
; mas normalmente palavras-chave (keywords) são melhores.
|
||||||
|
; Keywords são como strings mas com algumas vantagens.
|
||||||
|
(class :a) ; => clojure.lang.Keyword
|
||||||
|
|
||||||
|
(def stringmap {"a" 1, "b" 2, "c" 3})
|
||||||
|
stringmap ; => {"a" 1, "b" 2, "c" 3}
|
||||||
|
|
||||||
|
(def keymap {:a 1, :b 2, :c 3})
|
||||||
|
keymap ; => {:a 1, :c 3, :b 2}
|
||||||
|
|
||||||
|
; A propósito, vírgulas são sempre tratadas como espaçoes em branco e não fazem nada.
|
||||||
|
|
||||||
|
; Recupere o valor de um mapa chamando ele como uma função
|
||||||
|
(stringmap "a") ; => 1
|
||||||
|
(keymap :a) ; => 1
|
||||||
|
|
||||||
|
; Uma palavra-chave pode ser usada pra recuperar os valores de um mapa
|
||||||
|
(:b keymap) ; => 2
|
||||||
|
|
||||||
|
; Não tente isso com strings
|
||||||
|
;("a" stringmap)
|
||||||
|
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
|
||||||
|
|
||||||
|
; Buscar uma chave não presente retorna nil
|
||||||
|
(stringmap "d") ; => nil
|
||||||
|
|
||||||
|
; Use assoc para adicionar novas chaves para hash-maps
|
||||||
|
(def newkeymap (assoc keymap :d 4))
|
||||||
|
newkeymap ; => {:a 1, :b 2, :c 3, :d 4}
|
||||||
|
|
||||||
|
; Mas lembre-se, tipos em Clojure são sempre imutáveis!
|
||||||
|
keymap ; => {:a 1, :b 2, :c 3}
|
||||||
|
|
||||||
|
; Use dissoc para remover chaves
|
||||||
|
(dissoc keymap :a :b) ; => {:c 3}
|
||||||
|
|
||||||
|
; Conjuntos
|
||||||
|
;;;;;;
|
||||||
|
|
||||||
|
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
|
||||||
|
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
|
||||||
|
|
||||||
|
; Adicione um membro com conj
|
||||||
|
(conj #{1 2 3} 4) ; => #{1 2 3 4}
|
||||||
|
|
||||||
|
; Remova um membro com disj
|
||||||
|
(disj #{1 2 3} 1) ; => #{2 3}
|
||||||
|
|
||||||
|
; Test por existência usando set como função:
|
||||||
|
(#{1 2 3} 1) ; => 1
|
||||||
|
(#{1 2 3} 4) ; => nil
|
||||||
|
|
||||||
|
; Existem muitas outras funções no namespace clojure.sets
|
||||||
|
|
||||||
|
; Forms úteis
|
||||||
|
;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Construções lógicas em Clojure são como macros, e
|
||||||
|
; se parecem com as demais
|
||||||
|
(if false "a" "b") ; => "b"
|
||||||
|
(if false "a") ; => nil
|
||||||
|
|
||||||
|
; Use let para criar um novo escopo associando sîmbolos a valores (bindings)
|
||||||
|
(let [a 1 b 2]
|
||||||
|
(> a b)) ; => false
|
||||||
|
|
||||||
|
; Agrupe comandos juntos com "do"
|
||||||
|
(do
|
||||||
|
(print "Hello")
|
||||||
|
"World") ; => "World" (prints "Hello")
|
||||||
|
|
||||||
|
; Funções tem um do implícito
|
||||||
|
(defn print-and-say-hello [name]
|
||||||
|
(print "Saying hello to " name)
|
||||||
|
(str "Hello " name))
|
||||||
|
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
|
||||||
|
|
||||||
|
; Assim como let
|
||||||
|
(let [name "Urkel"]
|
||||||
|
(print "Saying hello to " name)
|
||||||
|
(str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
|
||||||
|
|
||||||
|
; Módulos
|
||||||
|
;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Use "use" para poder usar todas as funções de um modulo
|
||||||
|
(use 'clojure.set)
|
||||||
|
|
||||||
|
; Agora nós podemos usar operações com conjuntos
|
||||||
|
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
|
||||||
|
(difference #{1 2 3} #{2 3 4}) ; => #{1}
|
||||||
|
|
||||||
|
; Você pode escolher um subconjunto de funções para importar
|
||||||
|
(use '[clojure.set :only [intersection]])
|
||||||
|
|
||||||
|
; Use require para importar um módulo
|
||||||
|
(require 'clojure.string)
|
||||||
|
|
||||||
|
; Use / para chamar funções de um módulo
|
||||||
|
; Aqui, o módulo é clojure.string e a função é blank?
|
||||||
|
(clojure.string/blank? "") ; => true
|
||||||
|
|
||||||
|
; Você pode dar para um módulo um nome mais curto no import
|
||||||
|
(require '[clojure.string :as str])
|
||||||
|
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
|
||||||
|
; (#"" denota uma expressão regular literal)
|
||||||
|
|
||||||
|
; Você pode usar require (e até "use", mas escolha require) de um namespace utilizando :require.
|
||||||
|
; Não é necessário usar aspa simples nos seus módulos se você usar desse jeito.
|
||||||
|
(ns test
|
||||||
|
(:require
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.set :as set]))
|
||||||
|
|
||||||
|
; Java
|
||||||
|
;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Java tem uma biblioteca padrão enorme e muito útil,
|
||||||
|
; portanto é importante aprender como utiliza-la.
|
||||||
|
|
||||||
|
; Use import para carregar um modulo java
|
||||||
|
(import java.util.Date)
|
||||||
|
|
||||||
|
; Você pode importar usando ns também.
|
||||||
|
(ns test
|
||||||
|
(:import java.util.Date
|
||||||
|
java.util.Calendar))
|
||||||
|
|
||||||
|
; Use o nome da clase com um "." no final para criar uma nova instância
|
||||||
|
(Date.) ; <a date object>
|
||||||
|
|
||||||
|
; Use . para chamar métodos. Ou, use o atalho ".method"
|
||||||
|
(. (Date.) getTime) ; <a timestamp>
|
||||||
|
(.getTime (Date.)) ; exatamente a mesma coisa.
|
||||||
|
|
||||||
|
; Use / para chamar métodos estáticos
|
||||||
|
(System/currentTimeMillis) ; <a timestamp> (o módulo System está sempre presente)
|
||||||
|
|
||||||
|
; Use doto para pode lidar com classe (mutáveis) de forma mais tolerável
|
||||||
|
(import java.util.Calendar)
|
||||||
|
(doto (Calendar/getInstance)
|
||||||
|
(.set 2000 1 1 0 0 0)
|
||||||
|
.getTime) ; => A Date. set to 2000-01-01 00:00:00
|
||||||
|
|
||||||
|
; STM
|
||||||
|
;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Software Transactional Memory é o mecanismo que Clojure usa para gerenciar
|
||||||
|
; estado persistente. Tem algumas construções em Clojure que o utilizam.
|
||||||
|
|
||||||
|
; O atom é o mais simples. Passe pra ele um valor inicial
|
||||||
|
(def my-atom (atom {}))
|
||||||
|
|
||||||
|
; Atualize o atom com um swap!.
|
||||||
|
; swap! pega uma funçnao and chama ela com o valor atual do atom
|
||||||
|
; como primeiro argumento, e qualquer argumento restante como o segundo
|
||||||
|
(swap! my-atom assoc :a 1) ; Coloca o valor do átomo my-atom como o resultado de (assoc {} :a 1)
|
||||||
|
(swap! my-atom assoc :b 2) ; Coloca o valor do átomo my-atom como o resultado de (assoc {:a 1} :b 2)
|
||||||
|
|
||||||
|
; Use '@' para desreferenciar um atom e acessar seu valor
|
||||||
|
my-atom ;=> Atom<#...> (Retorna o objeto do Atom)
|
||||||
|
@my-atom ; => {:a 1 :b 2}
|
||||||
|
|
||||||
|
; Abaixo um contador simples usando um atom
|
||||||
|
(def counter (atom 0))
|
||||||
|
(defn inc-counter []
|
||||||
|
(swap! counter inc))
|
||||||
|
|
||||||
|
(inc-counter)
|
||||||
|
(inc-counter)
|
||||||
|
(inc-counter)
|
||||||
|
(inc-counter)
|
||||||
|
(inc-counter)
|
||||||
|
|
||||||
|
@counter ; => 5
|
||||||
|
|
||||||
|
; Outras construção STM são refs e agents.
|
||||||
|
; Refs: http://clojure.org/refs
|
||||||
|
; Agents: http://clojure.org/agents
|
||||||
|
```
|
||||||
|
|
||||||
|
### Leitura adicional
|
||||||
|
|
||||||
|
Esse tutorial está longe de ser exaustivo, mas deve ser suficiente para que você possa começar.
|
||||||
|
|
||||||
|
Clojure.org tem vários artigos:
|
||||||
|
[http://clojure.org/](http://clojure.org/)
|
||||||
|
|
||||||
|
Clojuredocs.org tem documentação com exemplos para quase todas as funções principais (pertecentes ao core):
|
||||||
|
[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core)
|
||||||
|
|
||||||
|
4Clojure é um grande jeito de aperfeiçoar suas habilidades em Clojure/Programação Funcional:
|
||||||
|
[http://www.4clojure.com/](http://www.4clojure.com/)
|
||||||
|
|
||||||
|
Clojure-doc.org tem um bom número de artigos para iniciantes:
|
||||||
|
[http://clojure-doc.org/](http://clojure-doc.org/)
|
500
pt-br/swift-pt.html.markdown
Normal file
500
pt-br/swift-pt.html.markdown
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
---
|
||||||
|
language: swift
|
||||||
|
contributors:
|
||||||
|
- ["Grant Timmerman", "http://github.com/grant"],
|
||||||
|
- ["Christopher Bess", "http://github.com/cbess"]
|
||||||
|
translators:
|
||||||
|
- ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"]
|
||||||
|
lang: pt-br
|
||||||
|
filename: learnswift.swift
|
||||||
|
---
|
||||||
|
|
||||||
|
Swift é uma linguagem de programação para desenvolvimento de aplicações no iOS e OS X criada pela Apple. Criada para
|
||||||
|
coexistir com Objective-C e para ser mais resiliente a código com erros, Swift foi apresentada em 2014 na Apple's
|
||||||
|
developer conference WWDC. Foi construída com o compilador LLVM já incluído no Xcode 6 beta.
|
||||||
|
|
||||||
|
O livro oficial [Swift Programming Language] (https://itunes.apple.com/us/book/swift-programming-language/id881256329) da
|
||||||
|
Apple já está disponível via IBooks (apenas em inglês).
|
||||||
|
|
||||||
|
Confira também o tutorial completo de Swift da Apple [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html), também disponível apenas em inglês.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// importa um módulo
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Noções básicas
|
||||||
|
//
|
||||||
|
|
||||||
|
// Xcode supporta anotações para seu código e lista elas na barra de atalhos
|
||||||
|
// MARK: Marca uma sessão
|
||||||
|
// TODO: Faça algo logo
|
||||||
|
// FIXME: Conserte esse código
|
||||||
|
|
||||||
|
println("Hello, world")
|
||||||
|
|
||||||
|
// Valores em variáveis (var) podem ter seu valor alterado depois de declarados.
|
||||||
|
// Valores em constantes (let) NÃO podem ser alterados depois de declarados.
|
||||||
|
|
||||||
|
var myVariable = 42
|
||||||
|
let øπΩ = "value" // nomes de variáveis em unicode
|
||||||
|
let π = 3.1415926
|
||||||
|
let convenience = "keyword" // nome de variável contextual
|
||||||
|
let weak = "keyword"; let override = "another keyword" // comandos podem ser separados por uma ponto e vírgula
|
||||||
|
let `class` = "keyword" // Crases permitem que palavras-chave seja usadas como nome de variáveis
|
||||||
|
let explicitDouble: Double = 70
|
||||||
|
let intValue = 0007 // 7
|
||||||
|
let largeIntValue = 77_000 // 77000
|
||||||
|
let label = "some text " + String(myVariable) // Coerção
|
||||||
|
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // Interpolação de strings
|
||||||
|
|
||||||
|
// Constrói valores específicos
|
||||||
|
// Utiliza configuração de build -D
|
||||||
|
#if false
|
||||||
|
println("Not printed")
|
||||||
|
let buildValue = 3
|
||||||
|
#else
|
||||||
|
let buildValue = 7
|
||||||
|
#endif
|
||||||
|
println("Build value: \(buildValue)") // Build value: 7
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optionals fazem parte da linguagem e permitem que você armazene um
|
||||||
|
valor `Some` (algo) ou `None` (nada).
|
||||||
|
|
||||||
|
Como Swift requer que todas as propriedades tenham valores, até mesmo nil deve
|
||||||
|
ser explicitamente armazenado como um valor Optional.
|
||||||
|
|
||||||
|
Optional<T> é uma enum.
|
||||||
|
*/
|
||||||
|
var someOptionalString: String? = "optional" // Pode ser nil
|
||||||
|
// o mesmo acima, mas ? é um operador pós-fixado (açúcar sintático)
|
||||||
|
var someOptionalString2: Optional<String> = "optional"
|
||||||
|
|
||||||
|
if someOptionalString != nil {
|
||||||
|
// Eu não sou nil
|
||||||
|
if someOptionalString!.hasPrefix("opt") {
|
||||||
|
println("has the prefix")
|
||||||
|
}
|
||||||
|
|
||||||
|
let empty = someOptionalString?.isEmpty
|
||||||
|
}
|
||||||
|
someOptionalString = nil
|
||||||
|
|
||||||
|
// Optional implicitamente desempacotado (unwrapped)
|
||||||
|
var unwrappedString: String! = "Valor é esperado."
|
||||||
|
// o mesmo acima, mas ? é um operador pósfixado (açúcar sintático)
|
||||||
|
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Valor é esperado."
|
||||||
|
|
||||||
|
if let someOptionalStringConstant = someOptionalString {
|
||||||
|
// Tem `Some` (algum) valor, não nil
|
||||||
|
if !someOptionalStringConstant.hasPrefix("ok") {
|
||||||
|
// não possui o prefixo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swift tem suporte para armazenar um valor de qualquer tipo.
|
||||||
|
// AnyObject == id
|
||||||
|
// Ao contrário de Objective-C `id`, AnyObject funciona com qualquer valor (Class, Int, struct, etc)
|
||||||
|
var anyObjectVar: AnyObject = 7
|
||||||
|
anyObjectVar = "Mudou o valor para string, não é uma boa prática, mas é possível."
|
||||||
|
|
||||||
|
/*
|
||||||
|
Comentário aqui
|
||||||
|
/*
|
||||||
|
Comentários aninhados também são suportados
|
||||||
|
*/
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Coleções
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tipos Array e Dicionário são structs. Portanto `let` e `var`
|
||||||
|
também indicam se são mutáveis (var) ou imutáveis (let) quando declarados
|
||||||
|
com esses tipos.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Array
|
||||||
|
var shoppingList = ["catfish", "water", "lemons"]
|
||||||
|
shoppingList[1] = "bottle of water"
|
||||||
|
let emptyArray = [String]() // imutável
|
||||||
|
var emptyMutableArray = [String]() // mutável
|
||||||
|
|
||||||
|
|
||||||
|
// Dicionário
|
||||||
|
var occupations = [
|
||||||
|
"Malcolm": "Captain",
|
||||||
|
"kaylee": "Mechanic"
|
||||||
|
]
|
||||||
|
occupations["Jayne"] = "Public Relations"
|
||||||
|
let emptyDictionary = [String: Float]() // imutável
|
||||||
|
var emptyMutableDictionary = [String: Float]() // mutável
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Controle de fluxo
|
||||||
|
//
|
||||||
|
|
||||||
|
// laço for (array)
|
||||||
|
let myArray = [1, 1, 2, 3, 5]
|
||||||
|
for value in myArray {
|
||||||
|
if value == 1 {
|
||||||
|
println("One!")
|
||||||
|
} else {
|
||||||
|
println("Not one!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// laço for (dicionário)
|
||||||
|
var dict = ["one": 1, "two": 2]
|
||||||
|
for (key, value) in dict {
|
||||||
|
println("\(key): \(value)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// laço for (alcance)
|
||||||
|
for i in -1...shoppingList.count {
|
||||||
|
println(i)
|
||||||
|
}
|
||||||
|
shoppingList[1...2] = ["steak", "peacons"]
|
||||||
|
// use ..< para excluir o último número
|
||||||
|
|
||||||
|
// laço while (enquanto)
|
||||||
|
var i = 1
|
||||||
|
while i < 1000 {
|
||||||
|
i *= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// laço do-while
|
||||||
|
do {
|
||||||
|
println("hello")
|
||||||
|
} while 1 == 2
|
||||||
|
|
||||||
|
// Switch
|
||||||
|
let vegetable = "red pepper"
|
||||||
|
switch vegetable {
|
||||||
|
case "celery":
|
||||||
|
let vegetableComment = "Add some raisins and make ants on a log."
|
||||||
|
case "cucumber", "watercress":
|
||||||
|
let vegetableComment = "That would make a good tea sandwich."
|
||||||
|
case let x where x.hasSuffix("pepper"):
|
||||||
|
let vegetableComment = "Is it a spicy \(x)?"
|
||||||
|
default: // required (in order to cover all possible input)
|
||||||
|
let vegetableComment = "Everything tastes good in soup."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Funções
|
||||||
|
//
|
||||||
|
|
||||||
|
// Funções são tipos de primeira classe, o que significa que eles podem ser aninhados
|
||||||
|
// em funções e podem ser passados como parâmetros
|
||||||
|
|
||||||
|
// Funções Swift com cabeçalhos doc (formato como reStructedText)
|
||||||
|
/**
|
||||||
|
Uma operação de saudação
|
||||||
|
|
||||||
|
- Um bullet em documentos
|
||||||
|
- Outro bullet
|
||||||
|
|
||||||
|
:param: nome A nome
|
||||||
|
:param: dia A dia
|
||||||
|
:returns: Uma string contendo o nome e o dia.
|
||||||
|
*/
|
||||||
|
func greet(name: String, day: String) -> String {
|
||||||
|
return "Hello \(name), today is \(day)."
|
||||||
|
}
|
||||||
|
greet("Bob", "Tuesday")
|
||||||
|
|
||||||
|
// Função que retorna múltiplos items em uma tupla
|
||||||
|
func getGasPrices() -> (Double, Double, Double) {
|
||||||
|
return (3.59, 3.69, 3.79)
|
||||||
|
}
|
||||||
|
let pricesTuple = getGasPrices()
|
||||||
|
let price = pricesTuple.2 // 3.79
|
||||||
|
// Ignore valores de Tuplas (ou outros valores) usando _ (underscore)
|
||||||
|
let (_, price1, _) = pricesTuple // price1 == 3.69
|
||||||
|
println(price1 == pricesTuple.1) // true
|
||||||
|
println("Gas price: \(price)")
|
||||||
|
|
||||||
|
// Número variável de argumentos
|
||||||
|
func setup(numbers: Int...) {
|
||||||
|
// its an array
|
||||||
|
let number = numbers[0]
|
||||||
|
let argCount = numbers.count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passando e retornando funções
|
||||||
|
func makeIncrementer() -> (Int -> Int) {
|
||||||
|
func addOne(number: Int) -> Int {
|
||||||
|
return 1 + number
|
||||||
|
}
|
||||||
|
return addOne
|
||||||
|
}
|
||||||
|
var increment = makeIncrementer()
|
||||||
|
increment(7)
|
||||||
|
|
||||||
|
// passagem por referência
|
||||||
|
func swapTwoInts(inout a: Int, inout b: Int) {
|
||||||
|
let tempA = a
|
||||||
|
a = b
|
||||||
|
b = tempA
|
||||||
|
}
|
||||||
|
var someIntA = 7
|
||||||
|
var someIntB = 3
|
||||||
|
swapTwoInts(&someIntA, &someIntB)
|
||||||
|
println(someIntB) // 7
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Closures
|
||||||
|
//
|
||||||
|
var numbers = [1, 2, 6]
|
||||||
|
|
||||||
|
// Funções são casos especiais de closures ({})
|
||||||
|
|
||||||
|
// Exemplo de closure.
|
||||||
|
// `->` separa argumentos e tipo de retorno
|
||||||
|
// `in` separa o cabeçalho do closure do seu corpo
|
||||||
|
numbers.map({
|
||||||
|
(number: Int) -> Int in
|
||||||
|
let result = 3 * number
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// Quando o tipo é conhecido, como abaixo, nós podemos fazer o seguinte
|
||||||
|
numbers = numbers.map({ number in 3 * number })
|
||||||
|
// Ou até mesmo isso
|
||||||
|
//numbers = numbers.map({ $0 * 3 })
|
||||||
|
|
||||||
|
print(numbers) // [3, 6, 18]
|
||||||
|
|
||||||
|
// Closure restante
|
||||||
|
numbers = sorted(numbers) { $0 > $1 }
|
||||||
|
|
||||||
|
print(numbers) // [18, 6, 3]
|
||||||
|
|
||||||
|
// Super atalho, já que o operador < infere os tipos
|
||||||
|
|
||||||
|
numbers = sorted(numbers, < )
|
||||||
|
|
||||||
|
print(numbers) // [3, 6, 18]
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Estruturas
|
||||||
|
//
|
||||||
|
|
||||||
|
// Estruturas e classes tem funcionalidades muito similares
|
||||||
|
struct NamesTable {
|
||||||
|
let names: [String]
|
||||||
|
|
||||||
|
// Custom subscript
|
||||||
|
subscript(index: Int) -> String {
|
||||||
|
return names[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estruturas possuem um inicializador auto-gerado automático (implícito)
|
||||||
|
let namesTable = NamesTable(names: ["Me", "Them"])
|
||||||
|
//let name = namesTable[2]
|
||||||
|
//println("Name is \(name)") // Name is Them
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Classes
|
||||||
|
//
|
||||||
|
|
||||||
|
// Classes, Estruturas e seus membros possuem três níveis de modificadores de acesso
|
||||||
|
// Eles são: internal (default), public, private
|
||||||
|
|
||||||
|
public class Shape {
|
||||||
|
public func getArea() -> Int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todos os métodos e propriedades de uma classe são públicos.
|
||||||
|
// Se você só precisa armazenar dados em um objeto estruturado, use `struct`
|
||||||
|
|
||||||
|
internal class Rect: Shape {
|
||||||
|
var sideLength: Int = 1
|
||||||
|
|
||||||
|
// Getter e setter personalizado
|
||||||
|
private var perimeter: Int {
|
||||||
|
get {
|
||||||
|
return 4 * sideLength
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
// `newValue` é uma variável implicita disponível para os setters
|
||||||
|
sideLength = newValue / 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carregue uma propriedade sob demanda (lazy)
|
||||||
|
// subShape permanece nil (não inicializado) até seu getter ser chamado
|
||||||
|
lazy var subShape = Rect(sideLength: 4)
|
||||||
|
|
||||||
|
// Se você não precisa de um getter e setter personalizado,
|
||||||
|
// mas ainda quer roda código antes e depois de configurar
|
||||||
|
// uma propriedade, você pode usar `willSet` e `didSet`
|
||||||
|
var identifier: String = "defaultID" {
|
||||||
|
// o argumento `willSet` será o nome da variável para o novo valor
|
||||||
|
willSet(someIdentifier) {
|
||||||
|
print(someIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(sideLength: Int) {
|
||||||
|
self.sideLength = sideLength
|
||||||
|
// sempre chame super.init por último quand inicializar propriedades personalizadas (custom)
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func shrink() {
|
||||||
|
if sideLength > 0 {
|
||||||
|
--sideLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func getArea() -> Int {
|
||||||
|
return sideLength * sideLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uma classe básica `Square` que estende `Rect`
|
||||||
|
class Square: Rect {
|
||||||
|
convenience init() {
|
||||||
|
self.init(sideLength: 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mySquare = Square()
|
||||||
|
print(mySquare.getArea()) // 25
|
||||||
|
mySquare.shrink()
|
||||||
|
print(mySquare.sideLength) // 4
|
||||||
|
|
||||||
|
// Compara instâncias, não é o mesmo que == o qual compara objetos
|
||||||
|
if mySquare === mySquare {
|
||||||
|
println("Yep, it's mySquare")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Enums
|
||||||
|
//
|
||||||
|
|
||||||
|
// Enums podem opcionalmente ser de um tipo específico ou não.
|
||||||
|
// Podem conter métodos do mesmo jeito que classes.
|
||||||
|
|
||||||
|
enum Suit {
|
||||||
|
case Spades, Hearts, Diamonds, Clubs
|
||||||
|
func getIcon() -> String {
|
||||||
|
switch self {
|
||||||
|
case .Spades: return "♤"
|
||||||
|
case .Hearts: return "♡"
|
||||||
|
case .Diamonds: return "♢"
|
||||||
|
case .Clubs: return "♧"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Protocolos
|
||||||
|
//
|
||||||
|
|
||||||
|
// `protocol` pode requerer que os tipos que se adequam tenham
|
||||||
|
// propriedades de instância, métodos, operadores e subscripts.
|
||||||
|
protocol ShapeGenerator {
|
||||||
|
var enabled: Bool { get set }
|
||||||
|
func buildShape() -> Shape
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocolos declarados com @objc permitem funções opcionais,
|
||||||
|
// que permitem verificar a confomidade
|
||||||
|
@objc protocol TransformShape {
|
||||||
|
optional func reshaped()
|
||||||
|
optional func canReshape() -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyShape: Rect {
|
||||||
|
var delegate: TransformShape?
|
||||||
|
|
||||||
|
func grow() {
|
||||||
|
sideLength += 2
|
||||||
|
|
||||||
|
if let allow = self.delegate?.canReshape?() {
|
||||||
|
// test for delegate then for method
|
||||||
|
// testa por delegação e então por método
|
||||||
|
self.delegate?.reshaped?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: Outros
|
||||||
|
//
|
||||||
|
|
||||||
|
// `extension`s: Adicionam uma funcionalidade extra para um tipo já existente.
|
||||||
|
|
||||||
|
// Square agora "segue" o protocolo `Printable`
|
||||||
|
extension Square: Printable {
|
||||||
|
var description: String {
|
||||||
|
return "Area: \(self.getArea()) - ID: \(self.identifier)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Square: \(mySquare)")
|
||||||
|
|
||||||
|
// Você pode também estender tipos embutidos (built-in)
|
||||||
|
extension Int {
|
||||||
|
var customProperty: String {
|
||||||
|
return "This is \(self)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiplyBy(num: Int) -> Int {
|
||||||
|
return num * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println(7.customProperty) // "This is 7"
|
||||||
|
println(14.multiplyBy(2)) // 42
|
||||||
|
|
||||||
|
// Generics: Similar com Java e C#. Use a palavra-chave `where` para
|
||||||
|
// especificar os requisitos do generics.
|
||||||
|
|
||||||
|
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
|
||||||
|
for (index, value) in enumerate(array) {
|
||||||
|
if value == valueToFind {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let foundAtIndex = findIndex([1, 2, 3, 4], 3)
|
||||||
|
println(foundAtIndex == 2) // true
|
||||||
|
|
||||||
|
// Operadores:
|
||||||
|
// Operadores personalizados (custom) podem começar com os seguintes caracteres:
|
||||||
|
// / = - + * % < > ! & | ^ . ~
|
||||||
|
// ou
|
||||||
|
// Unicode math, símbolo, seta, e caracteres tipográficos ou de desenho.
|
||||||
|
prefix operator !!! {}
|
||||||
|
|
||||||
|
// Um operador de prefixo que triplica o comprimento do lado do quadrado
|
||||||
|
// quando usado
|
||||||
|
prefix func !!! (inout shape: Square) -> Square {
|
||||||
|
shape.sideLength *= 3
|
||||||
|
return shape
|
||||||
|
}
|
||||||
|
|
||||||
|
// valor atual
|
||||||
|
println(mySquare.sideLength) // 4
|
||||||
|
|
||||||
|
// Troca o comprimento do lado usando um operador personalizado !!!, aumenta o lado por 3
|
||||||
|
!!!mySquare
|
||||||
|
println(mySquare.sideLength) // 12
|
||||||
|
|
||||||
|
```
|
@@ -264,7 +264,7 @@ filled_dict.get("four") # => None
|
|||||||
# The get method supports a default argument when the value is missing
|
# The get method supports a default argument when the value is missing
|
||||||
filled_dict.get("one", 4) # => 1
|
filled_dict.get("one", 4) # => 1
|
||||||
filled_dict.get("four", 4) # => 4
|
filled_dict.get("four", 4) # => 4
|
||||||
# note that filled_dict.get("four") is still => 4
|
# note that filled_dict.get("four") is still => None
|
||||||
# (get doesn't set the value in the dictionary)
|
# (get doesn't set the value in the dictionary)
|
||||||
|
|
||||||
# set the value of a key with a syntax similar to lists
|
# set the value of a key with a syntax similar to lists
|
||||||
|
@@ -635,7 +635,6 @@ print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :(
|
|||||||
|
|
||||||
* [The Official Docs](http://docs.python.org/3/)
|
* [The Official Docs](http://docs.python.org/3/)
|
||||||
* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/)
|
* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/)
|
||||||
* [Python Module of the Week](http://pymotw.com/3/)
|
|
||||||
* [A Crash Course in Python for Scientists](http://nbviewer.ipython.org/5920182)
|
* [A Crash Course in Python for Scientists](http://nbviewer.ipython.org/5920182)
|
||||||
|
|
||||||
### Dead Tree
|
### Dead Tree
|
||||||
|
@@ -65,7 +65,7 @@ func beyondHello() {
|
|||||||
learnTypes() // < y minutes, learn more!
|
learnTypes() // < y minutes, learn more!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция имеющая входные параметры и возврат нескольких значений.
|
// Функция, имеющая входные параметры и возвращающая несколько значений.
|
||||||
func learnMultiple(x, y int) (sum, prod int) {
|
func learnMultiple(x, y int) (sum, prod int) {
|
||||||
return x + y, x * y // Возврат двух значений.
|
return x + y, x * y // Возврат двух значений.
|
||||||
}
|
}
|
||||||
|
425
ru-ru/lua-ru.html.markdown
Normal file
425
ru-ru/lua-ru.html.markdown
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
---
|
||||||
|
language: lua
|
||||||
|
filename: learnlua-ru.lua
|
||||||
|
contributors:
|
||||||
|
- ["Tyler Neylon", "http://tylerneylon.com/"]
|
||||||
|
translators:
|
||||||
|
- ["Max Solomonov", "https://vk.com/solomonovmaksim"]
|
||||||
|
- ["Max Truhonin", "https://vk.com/maximmax42"]
|
||||||
|
- ["Konstantin Gromyko", "https://vk.com/id0x1765d79"]
|
||||||
|
- ["Stanislav Gromov", "https://vk.com/id156354391"]
|
||||||
|
lang: ru-ru
|
||||||
|
---
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Два дефиса начинают однострочный комментарий.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Добавление двух квадратных скобок
|
||||||
|
делает комментарий многострочным.
|
||||||
|
--]]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 1. Переменные, циклы и условия.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
num = 42 -- Все числа имеют тип double.
|
||||||
|
-- Не волнуйтесь, в 64-битных double 52 бита
|
||||||
|
-- отведено под хранение целой части числа;
|
||||||
|
-- точность не является проблемой для
|
||||||
|
-- целочисленных значений, занимающих меньше 52 бит.
|
||||||
|
|
||||||
|
s = 'walternate' -- Неизменные строки, как в Python.
|
||||||
|
t = "Двойные кавычки также приветствуются"
|
||||||
|
u = [[ Двойные квадратные скобки
|
||||||
|
начинают и заканчивают
|
||||||
|
многострочные значения.]]
|
||||||
|
t = nil -- Удаляет определение переменной t; в Lua есть сборка мусора.
|
||||||
|
|
||||||
|
-- Блоки обозначаются ключевыми словами, такими как do/end:
|
||||||
|
while num < 50 do
|
||||||
|
num = num + 1 -- Операторов ++ и += нет.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ветвление "если":
|
||||||
|
if num > 40 then
|
||||||
|
print('больше 40')
|
||||||
|
elseif s ~= 'walternate' then -- ~= обозначает "не равно".
|
||||||
|
-- Проверка равенства это ==, как в Python; работает для строк.
|
||||||
|
io.write('не больше 40\n') -- По умолчанию вывод в stdout.
|
||||||
|
else
|
||||||
|
-- По умолчанию переменные являются глобальными.
|
||||||
|
thisIsGlobal = 5 -- Стиль CamelСase является общим.
|
||||||
|
|
||||||
|
-- Как сделать переменную локальной:
|
||||||
|
local line = io.read() -- Считывает введённую строку.
|
||||||
|
|
||||||
|
-- Для конкатенации строк используется оператор .. :
|
||||||
|
print('Зима пришла, ' .. line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Неопределённые переменные возвращают nil.
|
||||||
|
-- Этот пример не является ошибочным:
|
||||||
|
foo = anUnknownVariable -- Теперь foo = nil.
|
||||||
|
|
||||||
|
aBoolValue = false
|
||||||
|
|
||||||
|
-- Только значения nil и false являются ложными; 0 и '' являются истинными!
|
||||||
|
if not aBoolValue then print('это значение ложно') end
|
||||||
|
|
||||||
|
-- Для 'or' и 'and' действует принцип "какой оператор дальше,
|
||||||
|
-- тот и применяется". Это действует аналогично оператору a?b:c в C/js:
|
||||||
|
ans = aBoolValue and 'yes' or 'no' --> 'no'
|
||||||
|
|
||||||
|
karlSum = 0
|
||||||
|
for i = 1, 100 do -- Здесь указан диапазон, ограниченный с двух сторон.
|
||||||
|
karlSum = karlSum + i
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Используйте "100, 1, -1" как нисходящий диапазон:
|
||||||
|
fredSum = 0
|
||||||
|
for j = 100, 1, -1 do fredSum = fredSum + j end
|
||||||
|
|
||||||
|
-- В основном, диапазон устроен так: начало, конец[, шаг].
|
||||||
|
|
||||||
|
-- Другая конструкция цикла:
|
||||||
|
repeat
|
||||||
|
print('путь будущего')
|
||||||
|
num = num - 1
|
||||||
|
until num == 0
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 2. Функции.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function fib(n)
|
||||||
|
if n < 2 then return n end
|
||||||
|
return fib(n - 2) + fib(n - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Вложенные и анонимные функции являются нормой:
|
||||||
|
function adder(x)
|
||||||
|
-- Возращаемая функция создаётся, когда вызывается функция adder,
|
||||||
|
-- и запоминает значение переменной x:
|
||||||
|
return function (y) return x + y end
|
||||||
|
end
|
||||||
|
a1 = adder(9)
|
||||||
|
a2 = adder(36)
|
||||||
|
print(a1(16)) --> 25
|
||||||
|
print(a2(64)) --> 100
|
||||||
|
|
||||||
|
-- Возвраты, вызовы функций и присвоения работают со списками,
|
||||||
|
-- которые могут иметь разную длину.
|
||||||
|
-- Лишние получатели принимают значение nil, а лишние значения игнорируются.
|
||||||
|
|
||||||
|
x, y, z = 1, 2, 3, 4
|
||||||
|
-- Теперь x = 1, y = 2, z = 3, а 4 просто отбрасывается.
|
||||||
|
|
||||||
|
function bar(a, b, c)
|
||||||
|
print(a, b, c)
|
||||||
|
return 4, 8, 15, 16, 23, 42
|
||||||
|
end
|
||||||
|
|
||||||
|
x, y = bar('zaphod') --> выводит "zaphod nil nil"
|
||||||
|
-- Теперь x = 4, y = 8, а значения 15..42 отбрасываются.
|
||||||
|
|
||||||
|
-- Функции могут быть локальными и глобальными. Эти строки делают одно и то же:
|
||||||
|
function f(x) return x * x end
|
||||||
|
f = function (x) return x * x end
|
||||||
|
|
||||||
|
-- Эти тоже:
|
||||||
|
local function g(x) return math.sin(x) end
|
||||||
|
local g = function(x) return math.sin(x) end
|
||||||
|
-- Эквивалентно для local function g(x)..., однако ссылки на g
|
||||||
|
-- в теле функции не будут работать, как ожидалось.
|
||||||
|
local g; g = function (x) return math.sin(x) end
|
||||||
|
-- 'local g' будет прототипом функции.
|
||||||
|
|
||||||
|
-- Кстати, тригонометрические функции работают с радианами.
|
||||||
|
|
||||||
|
-- Вызов функции с одним строковым параметром не требует круглых скобок:
|
||||||
|
print 'hello' -- Работает без ошибок.
|
||||||
|
|
||||||
|
-- Вызов функции с одним табличным параметром также
|
||||||
|
-- не требует круглых скобок (про таблицы в след. части):
|
||||||
|
print {} -- Тоже сработает.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 3. Таблицы.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Таблица = единственная составная структура данных в Lua;
|
||||||
|
-- представляет собой ассоциативный массив.
|
||||||
|
-- Подобно массивам в PHP или объектам в JS, они представляют собой
|
||||||
|
-- хеш-таблицы, которые также можно использовать в качестве списков.
|
||||||
|
|
||||||
|
|
||||||
|
-- Использование словарей:
|
||||||
|
|
||||||
|
-- Литералы имеют ключ по умолчанию:
|
||||||
|
t = {key1 = 'value1', key2 = false}
|
||||||
|
|
||||||
|
-- Строковые ключи используются, как в точечной нотации в JS:
|
||||||
|
print(t.key1) -- Печатает 'value1'.
|
||||||
|
t.newKey = {} -- Добавляет новую пару ключ/значение.
|
||||||
|
t.key2 = nil -- Удаляет key2 из таблицы.
|
||||||
|
|
||||||
|
-- Литеральная нотация для любого значения ключа (кроме nil):
|
||||||
|
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
|
||||||
|
print(u[6.28]) -- пишет "tau"
|
||||||
|
|
||||||
|
-- Ключ соответствует значению для чисел и строк, но при
|
||||||
|
-- использовании таблицы в качестве ключа берётся её экземпляр.
|
||||||
|
a = u['@!#'] -- Теперь a = 'qbert'.
|
||||||
|
b = u[{}] -- Вы могли ожидать 1729, но получится nil:
|
||||||
|
-- b = nil, т.к. ключ не будет найден.
|
||||||
|
-- Это произойдёт потому, что за ключ мы использовали не тот же самый объект,
|
||||||
|
-- который был использован для сохранения оригинального значения.
|
||||||
|
-- Поэтому строки и числа удобнее использовать в качестве ключей.
|
||||||
|
|
||||||
|
-- Вызов функции с одной таблицей в качестве аргумента
|
||||||
|
-- не требует круглых скобок:
|
||||||
|
function h(x) print(x.key1) end
|
||||||
|
h{key1 = 'Sonmi~451'} -- Печатает 'Sonmi~451'.
|
||||||
|
|
||||||
|
for key, val in pairs(u) do -- Цикл по таблице.
|
||||||
|
print(key, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- _G - это таблица со всеми глобалями.
|
||||||
|
print(_G['_G'] == _G) -- Печатает 'true'.
|
||||||
|
|
||||||
|
-- Использование таблиц, как списков / массивов:
|
||||||
|
|
||||||
|
-- Список значений с неявно заданными целочисленными ключами:
|
||||||
|
v = {'value1', 'value2', 1.21, 'gigawatts'}
|
||||||
|
for i = 1, #v do -- #v - размер списка v.
|
||||||
|
print(v[i]) -- Нумерация начинается с 1 !!
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Список не является отдельным типом. v - всего лишь таблица
|
||||||
|
-- с последовательными целочисленными ключами, воспринимаемая как список.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 3.1 Метатаблицы и метаметоды.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Таблицу можно связать с метатаблицей, задав ей поведение, как при
|
||||||
|
-- перегрузке операторов. Позже мы увидим, что метатаблицы поддерживают
|
||||||
|
-- поведение, как в js-прототипах.
|
||||||
|
f1 = {a = 1, b = 2} -- Представляет дробь a/b.
|
||||||
|
f2 = {a = 2, b = 3}
|
||||||
|
|
||||||
|
-- Это не сработает:
|
||||||
|
-- s = f1 + f2
|
||||||
|
|
||||||
|
metafraction = {}
|
||||||
|
function metafraction.__add(f1, f2)
|
||||||
|
local sum = {}
|
||||||
|
sum.b = f1.b * f2.b
|
||||||
|
sum.a = f1.a * f2.b + f2.a * f1.b
|
||||||
|
return sum
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(f1, metafraction)
|
||||||
|
setmetatable(f2, metafraction)
|
||||||
|
|
||||||
|
s = f1 + f2 -- вызвать __add(f1, f2) на метатаблице от f1
|
||||||
|
|
||||||
|
-- f1, f2 не имеют ключа для своих метатаблиц в отличии от прототипов в js,
|
||||||
|
-- нужно получить его через getmetatable(f1). Метатаблица - обычная таблица
|
||||||
|
-- поэтому с ключами, известными для Lua (например, __add).
|
||||||
|
|
||||||
|
-- Но следущая строка будет ошибочной т.к в s нет метатаблицы:
|
||||||
|
-- t = s + s
|
||||||
|
-- Похожий на классы подход, приведенный ниже, поможет это исправить.
|
||||||
|
|
||||||
|
-- __index перегружает в метатаблице просмотр через точку:
|
||||||
|
defaultFavs = {animal = 'gru', food = 'donuts'}
|
||||||
|
myFavs = {food = 'pizza'}
|
||||||
|
setmetatable(myFavs, {__index = defaultFavs})
|
||||||
|
eatenBy = myFavs.animal -- работает! спасибо, мета-таблица.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- При неудаче прямой табличный поиск попытается использовать
|
||||||
|
-- значение __index в метатаблице, причём это рекурсивно.
|
||||||
|
|
||||||
|
-- Значение __index также может быть функцией
|
||||||
|
-- function(tbl, key) для настраиваемого поиска.
|
||||||
|
|
||||||
|
-- Значения типа __index, __add, ... называются метаметодами.
|
||||||
|
-- Ниже приведён полный список метаметодов.
|
||||||
|
|
||||||
|
-- __add(a, b) для a + b
|
||||||
|
-- __sub(a, b) для a - b
|
||||||
|
-- __mul(a, b) для a * b
|
||||||
|
-- __div(a, b) для a / b
|
||||||
|
-- __mod(a, b) для a % b
|
||||||
|
-- __pow(a, b) для a ^ b
|
||||||
|
-- __unm(a) для -a
|
||||||
|
-- __concat(a, b) для a .. b
|
||||||
|
-- __len(a) для #a
|
||||||
|
-- __eq(a, b) для a == b
|
||||||
|
-- __lt(a, b) для a < b
|
||||||
|
-- __le(a, b) для a <= b
|
||||||
|
-- __index(a, b) <функция или таблица> для a.b
|
||||||
|
-- __newindex(a, b, c) для a.b = c
|
||||||
|
-- __call(a, ...) для a(...)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 3.2 Классоподобные таблицы и наследование.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- В Lua нет поддержки классов на уровне языка,
|
||||||
|
-- однако существуют разные способы их создания с помощью
|
||||||
|
-- таблиц и метатаблиц.
|
||||||
|
|
||||||
|
-- Ниже приведён один из таких способов.
|
||||||
|
|
||||||
|
Dog = {} -- 1.
|
||||||
|
|
||||||
|
function Dog:new() -- 2.
|
||||||
|
local newObj = {sound = 'woof'} -- 3.
|
||||||
|
self.__index = self -- 4.
|
||||||
|
return setmetatable(newObj, self) -- 5.
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dog:makeSound() -- 6.
|
||||||
|
print('I say ' .. self.sound)
|
||||||
|
end
|
||||||
|
|
||||||
|
mrDog = Dog:new() -- 7.
|
||||||
|
mrDog:makeSound() -- 'I say woof' -- 8.
|
||||||
|
|
||||||
|
-- 1. Dog похоже на класс, но на самом деле это таблица.
|
||||||
|
-- 2. "function tablename:fn(...)" - то же самое, что и
|
||||||
|
-- "function tablename.fn(self, ...)", просто : добавляет первый аргумент
|
||||||
|
-- перед собой. См. пункты 7 и 8, чтобы понять, как self получает значение.
|
||||||
|
-- 3. newObj - это экземпляр класса Dog.
|
||||||
|
-- 4. "self" - экземпляр класса. Зачастую self = Dog, но с помощью наследования
|
||||||
|
-- это можно изменить. newObj получит свои функции, когда мы установим
|
||||||
|
-- метатаблицу для newObj и __index для self на саму себя.
|
||||||
|
-- 5. Напоминание: setmetatable возвращает первый аргумент.
|
||||||
|
-- 6. : работает, как в пункте 2, но в этот раз мы ожидаем,
|
||||||
|
-- что self будет экземпляром, а не классом.
|
||||||
|
-- 7. То же самое, что и Dog.new(Dog), поэтому self = Dog в new().
|
||||||
|
-- 8. То же самое, что mrDog.makeSound(mrDog); self = mrDog.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Пример наследования:
|
||||||
|
|
||||||
|
LoudDog = Dog:new() -- 1.
|
||||||
|
|
||||||
|
function LoudDog:makeSound()
|
||||||
|
local s = self.sound .. ' ' -- 2.
|
||||||
|
print(s .. s .. s)
|
||||||
|
end
|
||||||
|
|
||||||
|
seymour = LoudDog:new() -- 3.
|
||||||
|
seymour:makeSound() -- 'woof woof woof' -- 4.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 1. LoudDog получит методы и переменные класса Dog.
|
||||||
|
-- 2. В self будет ключ 'sound' из new(), см. пункт 3.
|
||||||
|
-- 3. То же самое, что и "LoudDog.new(LoudDog)", конвертированное
|
||||||
|
-- в "Dog.new(LoudDog)", поскольку в LoudDog нет ключа 'new',
|
||||||
|
-- но в его метатаблице есть "__index = Dog".
|
||||||
|
-- Результат: Метатаблицей для seymour стала LoudDog,
|
||||||
|
-- а "LoudDog.__index = Dog". Поэтому seymour.key будет равно
|
||||||
|
-- seymour.key, LoudDog.key, Dog.key, в зависимости от того,
|
||||||
|
-- какая таблица будет первой с заданным ключом.
|
||||||
|
-- 4. Ключ 'makeSound' находится в LoudDog;
|
||||||
|
-- то же самое, что и "LoudDog.makeSound(seymour)".
|
||||||
|
|
||||||
|
-- При необходимости функция new() в подклассе
|
||||||
|
-- может быть похожа на аналог в базовом классе.
|
||||||
|
function LoudDog:new()
|
||||||
|
local newObj = {}
|
||||||
|
-- установить newObj
|
||||||
|
self.__index = self
|
||||||
|
return setmetatable(newObj, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- 4. Модули.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
--[[ Я закомментировал этот раздел, чтобы остальная часть скрипта осталась
|
||||||
|
-- работоспособной.
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Предположим, файл mod.lua будет выглядеть так:
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function sayMyName()
|
||||||
|
print('Hrunkner')
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.sayHello()
|
||||||
|
print('Привет, ')
|
||||||
|
sayMyName()
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
|
-- Другой файл может использовать функционал mod.lua:
|
||||||
|
local mod = require('mod') -- Запустим файл mod.lua.
|
||||||
|
|
||||||
|
-- require - стандартный способ подключения модулей.
|
||||||
|
-- require ведёт себя так: (если не кэшировано, см. ниже)
|
||||||
|
local mod = (function ()
|
||||||
|
<содержимое mod.lua>
|
||||||
|
end)()
|
||||||
|
-- Файл mod.lua воспринимается, как тело функции, поэтому
|
||||||
|
-- все локальные переменные и функции внутри него не видны за его пределами.
|
||||||
|
|
||||||
|
-- Это работает, так как здесь mod = M в mod.lua:
|
||||||
|
mod.sayHello() -- Выведет "Привет, Hrunkner".
|
||||||
|
|
||||||
|
-- Это будет ошибочным; sayMyName доступна только в mod.lua:
|
||||||
|
mod.sayMyName() -- ошибка
|
||||||
|
|
||||||
|
-- Значения, возвращаемые require, кэшируются,
|
||||||
|
-- поэтому содержимое файла выполняется только 1 раз,
|
||||||
|
-- даже если он подключается с помощью require много раз.
|
||||||
|
|
||||||
|
-- Предположим, mod2.lua содержит "print('Hi!')".
|
||||||
|
local a = require('mod2') -- Выведет "Hi!"
|
||||||
|
local b = require('mod2') -- Ничего не выведет; a=b.
|
||||||
|
|
||||||
|
-- dofile, в отличии от require, работает без кэширования:
|
||||||
|
dofile('mod2') --> Hi!
|
||||||
|
dofile('mod2') --> Hi! (запустится снова)
|
||||||
|
|
||||||
|
-- loadfile загружает файл, но не запускает его.
|
||||||
|
f = loadfile('mod2') -- Вызов f() запустит содержимое mod2.lua.
|
||||||
|
|
||||||
|
-- loadstring - это loadfile для строк.
|
||||||
|
g = loadstring('print(343)') -- Вернет функцию.
|
||||||
|
g() -- Напишет 343.
|
||||||
|
|
||||||
|
--]]
|
||||||
|
|
||||||
|
```
|
||||||
|
## Примечание (от автора)
|
||||||
|
|
||||||
|
Мне было интересно изучить Lua, чтобы делать игры при помощи <a href="http://love2d.org/">игрового движка LÖVE</a>.
|
||||||
|
|
||||||
|
Я начинал с <a href="http://nova-fusion.com/2012/08/27/lua-for-programmers-part-1/">BlackBulletIV's Lua for programmers</a>.
|
||||||
|
Затем я прочитал официальную <a href="http://www.lua.org/pil/contents.html">Документацию по Lua</a>.
|
||||||
|
|
||||||
|
Также может быть полезной <a href="http://lua-users.org/files/wiki_insecure/users/thomasl/luarefv51.pdf">Краткая справка по Lua</a> на lua-users.org.
|
||||||
|
|
||||||
|
Ещё из основных тем не охвачены стандартные библиотеки:
|
||||||
|
|
||||||
|
* <a href="http://lua-users.org/wiki/StringLibraryTutorial">библиотека string</a>
|
||||||
|
* <a href="http://lua-users.org/wiki/TableLibraryTutorial">библиотека table</a>
|
||||||
|
* <a href="http://lua-users.org/wiki/MathLibraryTutorial">библиотека math</a>
|
||||||
|
* <a href="http://lua-users.org/wiki/IoLibraryTutorial">библиотека io</a>
|
||||||
|
* <a href="http://lua-users.org/wiki/OsLibraryTutorial">библиотека os</a>
|
||||||
|
|
||||||
|
Кстати, весь файл написан на Lua; сохраните его как learn.lua и запустите при помощи "lua learn.lua" !
|
||||||
|
|
||||||
|
Изначально эта статья была написана для tylerneylon.com.
|
||||||
|
Также она доступна как <a href="https://gist.github.com/tylerneylon/5853042">github gist</a>. Удачи с Lua!
|
@@ -198,8 +198,10 @@ weirdSum(2, 4) // => 16
|
|||||||
|
|
||||||
|
|
||||||
// The return keyword exists in Scala, but it only returns from the inner-most
|
// The return keyword exists in Scala, but it only returns from the inner-most
|
||||||
// def that surrounds it. It has no effect on anonymous functions. For example:
|
// def that surrounds it.
|
||||||
def foo(x: Int) = {
|
// WARNING: Using return in Scala is error-prone and should be avoided.
|
||||||
|
// It has no effect on anonymous functions. For example:
|
||||||
|
def foo(x: Int): Int = {
|
||||||
val anonFunc: Int => Int = { z =>
|
val anonFunc: Int => Int = { z =>
|
||||||
if (z > 5)
|
if (z > 5)
|
||||||
return z // This line makes z the return value of foo!
|
return z // This line makes z the return value of foo!
|
||||||
@@ -405,41 +407,55 @@ val otherGeorge = george.copy(phoneNumber = "9876")
|
|||||||
// 6. Pattern Matching
|
// 6. Pattern Matching
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
val me = Person("George", "1234")
|
// Pattern matching is a powerful and commonly used feature in Scala. Here's how
|
||||||
|
// you pattern match a case class. NB: Unlike other languages, Scala cases do
|
||||||
|
// not need breaks, fall-through does not happen.
|
||||||
|
|
||||||
me match { case Person(name, number) => {
|
def matchPerson(person: Person): String = person match {
|
||||||
"We matched someone : " + name + ", phone : " + number }}
|
// Then you specify the patterns:
|
||||||
|
case Person("George", number) => "We found George! His number is " + number
|
||||||
me match { case Person(name, number) => "Match : " + name; case _ => "Hm..." }
|
case Person("Kate", number) => "We found Kate! Her number is " + number
|
||||||
|
case Person(name, number) => "We matched someone : " + name + ", phone : " + number
|
||||||
me match { case Person("George", number) => "Match"; case _ => "Hm..." }
|
|
||||||
|
|
||||||
me match { case Person("Kate", number) => "Match"; case _ => "Hm..." }
|
|
||||||
|
|
||||||
me match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
|
|
||||||
|
|
||||||
val kate = Person("Kate", "1234")
|
|
||||||
|
|
||||||
kate match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Regular expressions
|
|
||||||
val email = "(.*)@(.*)".r // Invoking r on String makes it a Regex
|
|
||||||
val serialKey = """(\d{5})-(\d{5})-(\d{5})-(\d{5})""".r // Using verbatim (multiline) syntax
|
|
||||||
|
|
||||||
val matcher = (value: String) => {
|
|
||||||
println(value match {
|
|
||||||
case email(name, domain) => s"It was an email: $name"
|
|
||||||
case serialKey(p1, p2, p3, p4) => s"Serial key: $p1, $p2, $p3, $p4"
|
|
||||||
case _ => s"No match on '$value'" // default if no match found
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher("mrbean@pyahoo.com") // => "It was an email: mrbean"
|
val email = "(.*)@(.*)".r // Define a regex for the next example.
|
||||||
matcher("nope..") // => "No match on 'nope..'"
|
|
||||||
matcher("52917") // => "No match on '52917'"
|
// Pattern matching might look familiar to the switch statements in the C family
|
||||||
matcher("52752-16432-22178-47917") // => "Serial key: 52752, 16432, 22178, 47917"
|
// of languages, but this is much more powerful. In Scala, you can match much
|
||||||
|
// more:
|
||||||
|
def matchEverything(obj: Any): String = obj match {
|
||||||
|
// You can match values:
|
||||||
|
case "Hello world" => "Got the string Hello world"
|
||||||
|
|
||||||
|
// You can match by type:
|
||||||
|
case x: Double => "Got a Double: " + x
|
||||||
|
|
||||||
|
// You can specify conditions:
|
||||||
|
case x: Int if x > 10000 => "Got a pretty big number!"
|
||||||
|
|
||||||
|
// You can match case classes as before:
|
||||||
|
case Person(name, number) => s"Got contact info for $name!"
|
||||||
|
|
||||||
|
// You can match regular expressions:
|
||||||
|
case email(name, domain) => s"Got email address $name@$domain"
|
||||||
|
|
||||||
|
// You can match tuples:
|
||||||
|
case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c"
|
||||||
|
|
||||||
|
// You can match data structures:
|
||||||
|
case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c"
|
||||||
|
|
||||||
|
// You can nest patterns:
|
||||||
|
case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
|
||||||
|
}
|
||||||
|
|
||||||
|
// In fact, you can pattern match any object with an "unapply" method. This
|
||||||
|
// feature is so powerful that Scala lets you define whole functions as
|
||||||
|
// patterns:
|
||||||
|
val patternFunc: Person => String = {
|
||||||
|
case Person("George", number") => s"George's number: $number"
|
||||||
|
case Person(name, number) => s"Random person's number: $number"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
@@ -476,7 +492,7 @@ sSquared.reduce (_+_)
|
|||||||
// The filter function takes a predicate (a function from A -> Boolean) and
|
// The filter function takes a predicate (a function from A -> Boolean) and
|
||||||
// selects all elements which satisfy the predicate
|
// selects all elements which satisfy the predicate
|
||||||
List(1, 2, 3) filter (_ > 2) // List(3)
|
List(1, 2, 3) filter (_ > 2) // List(3)
|
||||||
case class Person(name:String, phoneNumber:String)
|
case class Person(name:String, age:Int)
|
||||||
List(
|
List(
|
||||||
Person(name = "Dom", age = 23),
|
Person(name = "Dom", age = 23),
|
||||||
Person(name = "Bob", age = 30)
|
Person(name = "Bob", age = 30)
|
||||||
|
@@ -6,7 +6,7 @@ contributors:
|
|||||||
filename: learnswift.swift
|
filename: learnswift.swift
|
||||||
---
|
---
|
||||||
|
|
||||||
Swift is a programming language for iOS and OS X development created by Apple. Designed to coexist with Objective-C and to be more resilient against erroneous code, Swift was introduced in 2014 at Apple's developer conference WWDC. It is built with the LLVM compiler included in Xcode 6 beta.
|
Swift is a programming language for iOS and OS X development created by Apple. Designed to coexist with Objective-C and to be more resilient against erroneous code, Swift was introduced in 2014 at Apple's developer conference WWDC. It is built with the LLVM compiler included in Xcode 6+.
|
||||||
|
|
||||||
The official [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) book from Apple is now available via iBooks.
|
The official [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) book from Apple is now available via iBooks.
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import UIKit
|
|||||||
// Xcode supports landmarks to annotate your code and lists them in the jump bar
|
// Xcode supports landmarks to annotate your code and lists them in the jump bar
|
||||||
// MARK: Section mark
|
// MARK: Section mark
|
||||||
// TODO: Do something soon
|
// TODO: Do something soon
|
||||||
// FIXME Fix this code
|
// FIXME: Fix this code
|
||||||
|
|
||||||
println("Hello, world")
|
println("Hello, world")
|
||||||
|
|
||||||
@@ -95,6 +95,7 @@ anyObjectVar = "Changed value to a string, not good practice, but possible."
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Comment here
|
Comment here
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Nested comments are also supported
|
Nested comments are also supported
|
||||||
*/
|
*/
|
||||||
@@ -112,8 +113,9 @@ Comment here
|
|||||||
// Array
|
// Array
|
||||||
var shoppingList = ["catfish", "water", "lemons"]
|
var shoppingList = ["catfish", "water", "lemons"]
|
||||||
shoppingList[1] = "bottle of water"
|
shoppingList[1] = "bottle of water"
|
||||||
let emptyArray = [String]() // immutable
|
let emptyArray = [String]() // let == immutable
|
||||||
var emptyMutableArray = [String]() // mutable
|
let emptyArray2 = Array<String>() // same as above
|
||||||
|
var emptyMutableArray = [String]() // var == mutable
|
||||||
|
|
||||||
|
|
||||||
// Dictionary
|
// Dictionary
|
||||||
@@ -122,8 +124,9 @@ var occupations = [
|
|||||||
"kaylee": "Mechanic"
|
"kaylee": "Mechanic"
|
||||||
]
|
]
|
||||||
occupations["Jayne"] = "Public Relations"
|
occupations["Jayne"] = "Public Relations"
|
||||||
let emptyDictionary = [String: Float]() // immutable
|
let emptyDictionary = [String: Float]() // let == immutable
|
||||||
var emptyMutableDictionary = [String: Float]() // mutable
|
let emptyDictionary2 = Dictionary<String, Float>() // same as above
|
||||||
|
var emptyMutableDictionary = [String: Float]() // var == mutable
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -165,14 +168,16 @@ do {
|
|||||||
} while 1 == 2
|
} while 1 == 2
|
||||||
|
|
||||||
// Switch
|
// Switch
|
||||||
|
// Very powerful, think `if` statements with syntax candy
|
||||||
|
// They support String, object instances, and primitives (Int, Double, etc)
|
||||||
let vegetable = "red pepper"
|
let vegetable = "red pepper"
|
||||||
switch vegetable {
|
switch vegetable {
|
||||||
case "celery":
|
case "celery":
|
||||||
let vegetableComment = "Add some raisins and make ants on a log."
|
let vegetableComment = "Add some raisins and make ants on a log."
|
||||||
case "cucumber", "watercress":
|
case "cucumber", "watercress":
|
||||||
let vegetableComment = "That would make a good tea sandwich."
|
let vegetableComment = "That would make a good tea sandwich."
|
||||||
case let x where x.hasSuffix("pepper"):
|
case let localScopeValue where localScopeValue.hasSuffix("pepper"):
|
||||||
let vegetableComment = "Is it a spicy \(x)?"
|
let vegetableComment = "Is it a spicy \(localScopeValue)?"
|
||||||
default: // required (in order to cover all possible input)
|
default: // required (in order to cover all possible input)
|
||||||
let vegetableComment = "Everything tastes good in soup."
|
let vegetableComment = "Everything tastes good in soup."
|
||||||
}
|
}
|
||||||
@@ -186,6 +191,7 @@ default: // required (in order to cover all possible input)
|
|||||||
// in functions and can be passed around
|
// in functions and can be passed around
|
||||||
|
|
||||||
// Function with Swift header docs (format as reStructedText)
|
// Function with Swift header docs (format as reStructedText)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A greet operation
|
A greet operation
|
||||||
|
|
||||||
@@ -201,6 +207,12 @@ func greet(name: String, day: String) -> String {
|
|||||||
}
|
}
|
||||||
greet("Bob", "Tuesday")
|
greet("Bob", "Tuesday")
|
||||||
|
|
||||||
|
// similar to above except for the function parameter behaviors
|
||||||
|
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
|
||||||
|
return "Hello \(requiredName), the day is \(localParamName)"
|
||||||
|
}
|
||||||
|
greet2(requiredName:"John", externalParamName: "Sunday")
|
||||||
|
|
||||||
// Function that returns multiple items in a tuple
|
// Function that returns multiple items in a tuple
|
||||||
func getGasPrices() -> (Double, Double, Double) {
|
func getGasPrices() -> (Double, Double, Double) {
|
||||||
return (3.59, 3.69, 3.79)
|
return (3.59, 3.69, 3.79)
|
||||||
@@ -281,7 +293,7 @@ print(numbers) // [3, 6, 18]
|
|||||||
|
|
||||||
// Structures and classes have very similar capabilites
|
// Structures and classes have very similar capabilites
|
||||||
struct NamesTable {
|
struct NamesTable {
|
||||||
let names: [String]
|
let names = [String]()
|
||||||
|
|
||||||
// Custom subscript
|
// Custom subscript
|
||||||
subscript(index: Int) -> String {
|
subscript(index: Int) -> String {
|
||||||
@@ -291,8 +303,8 @@ struct NamesTable {
|
|||||||
|
|
||||||
// Structures have an auto-generated (implicit) designated initializer
|
// Structures have an auto-generated (implicit) designated initializer
|
||||||
let namesTable = NamesTable(names: ["Me", "Them"])
|
let namesTable = NamesTable(names: ["Me", "Them"])
|
||||||
//let name = namesTable[2]
|
let name = namesTable[1]
|
||||||
//println("Name is \(name)") // Name is Them
|
println("Name is \(name)") // Name is Them
|
||||||
|
|
||||||
//
|
//
|
||||||
// MARK: Classes
|
// MARK: Classes
|
||||||
@@ -368,6 +380,9 @@ print(mySquare.getArea()) // 25
|
|||||||
mySquare.shrink()
|
mySquare.shrink()
|
||||||
print(mySquare.sideLength) // 4
|
print(mySquare.sideLength) // 4
|
||||||
|
|
||||||
|
// cast instance
|
||||||
|
let aShape = mySquare as Shape
|
||||||
|
|
||||||
// compare instances, not the same as == which compares objects (equal to)
|
// compare instances, not the same as == which compares objects (equal to)
|
||||||
if mySquare === mySquare {
|
if mySquare === mySquare {
|
||||||
println("Yep, it's mySquare")
|
println("Yep, it's mySquare")
|
||||||
@@ -393,6 +408,17 @@ enum Suit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enum values allow short hand syntax, no need to type the enum type
|
||||||
|
// when the variable is explicitly declared
|
||||||
|
var suitValue: Suit = .Hearts
|
||||||
|
|
||||||
|
// Non-Integer enums require direct raw value assignments
|
||||||
|
enum BookName: String {
|
||||||
|
case John = "John"
|
||||||
|
case Luke = "Luke"
|
||||||
|
}
|
||||||
|
println("Name: \(BookName.John.rawValue)")
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MARK: Protocols
|
// MARK: Protocols
|
||||||
@@ -490,5 +516,4 @@ println(mySquare.sideLength) // 4
|
|||||||
// change side length using custom !!! operator, increases size by 3
|
// change side length using custom !!! operator, increases size by 3
|
||||||
!!!mySquare
|
!!!mySquare
|
||||||
println(mySquare.sideLength) // 12
|
println(mySquare.sideLength) // 12
|
||||||
|
|
||||||
```
|
```
|
||||||
|
447
tcl.html.markdown
Executable file
447
tcl.html.markdown
Executable file
@@ -0,0 +1,447 @@
|
|||||||
|
---
|
||||||
|
language: Tcl
|
||||||
|
contributors:
|
||||||
|
- ["Poor Yorick", "http://pooryorick.com/"]
|
||||||
|
filename: learntcl.tcl
|
||||||
|
---
|
||||||
|
|
||||||
|
Tcl was created by [John Ousterhout](http://wiki.tcl.tk/John Ousterout) as a
|
||||||
|
reusable scripting language for chip design tools he was creating. In 1997 he
|
||||||
|
was awarded the [ACM Software System
|
||||||
|
Award](http://en.wikipedia.org/wiki/ACM_Software_System_Award) for Tcl. Tcl
|
||||||
|
can be used both as an embeddable scripting language and as a general
|
||||||
|
programming language. It can also be used as a portable C library, even in
|
||||||
|
cases where no scripting capability is needed, as it provides data structures
|
||||||
|
such as dynamic strings, lists, and hash tables. The C library also provides
|
||||||
|
portable functionality for loading dynamic libraries, string formatting and
|
||||||
|
code conversion, filesystem operations, network operations, and more.
|
||||||
|
Various features of Tcl stand out:
|
||||||
|
|
||||||
|
* Convenient cross-platform networking API
|
||||||
|
|
||||||
|
* Fully virtualized filesystem
|
||||||
|
|
||||||
|
* Stackable I/O channels
|
||||||
|
|
||||||
|
* Asynchronous to the core
|
||||||
|
|
||||||
|
* Full coroutines
|
||||||
|
|
||||||
|
* A threading model recognized as robust and easy to use
|
||||||
|
|
||||||
|
|
||||||
|
If Lisp is a list processor, then Tcl is a string processor. All values are
|
||||||
|
strings. A list is a string format. A procedure definition is a string
|
||||||
|
format. To achieve performance, Tcl internally caches structured
|
||||||
|
representations of these values. The list commands, for example, operate on
|
||||||
|
the internal cached representation, and Tcl takes care of updating the string
|
||||||
|
representation if it is ever actually needed in the script. The copy-on-write
|
||||||
|
design of Tcl allows script authors can pass around large data values without
|
||||||
|
actually incurring additional memory overhead. Procedures are automatically
|
||||||
|
byte-compiled unless they use the more dynamic commands such as "uplevel",
|
||||||
|
"upvar", and "trace".
|
||||||
|
|
||||||
|
Tcl is a pleasure to program in. It will appeal to hacker types who find Lisp,
|
||||||
|
Forth, or Smalltalk interesting, as well as to engineers and scientists who
|
||||||
|
just want to get down to business with a tool that bends to their will. Its
|
||||||
|
discipline of exposing all programmatic functionality as commands, including
|
||||||
|
things like loops and mathematical operations that are usually baked into the
|
||||||
|
syntax of other languages, allows it to fade into the background of whatever
|
||||||
|
domain-specific functionality a project needs. It's syntax, which is even
|
||||||
|
lighter that that of Lisp, just gets out of the way.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```tcl
|
||||||
|
#! /bin/env tclsh
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## 1. Guidelines
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Tcl is not Bash or C! This needs to be said because standard shell quoting
|
||||||
|
# habits almost work in Tcl and it is common for people to pick up Tcl and try
|
||||||
|
# to get by with syntax they know from another language. It works at first,
|
||||||
|
# but soon leads to frustration with more complex scripts.
|
||||||
|
|
||||||
|
# Braces are just a quoting mechanism, not a code block constructor or a list
|
||||||
|
# constructor. Tcl doesn't have either of those things. Braces are used,
|
||||||
|
# though, to escape special characters in procedure bodies and in strings that
|
||||||
|
# are formatted as lists.
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## 2. Syntax
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Every line is a command. The first word is the name of the command, and
|
||||||
|
# subsequent words are arguments to the command. Words are delimited by
|
||||||
|
# whitespace. Since every word is a string, in the simple case no special
|
||||||
|
# markup such as quotes, braces, or backslash, is necessary. Even when quotes
|
||||||
|
# are used, they are not a string constructor, but just another escaping
|
||||||
|
# character.
|
||||||
|
|
||||||
|
set greeting1 Sal
|
||||||
|
set greeting2 ut
|
||||||
|
set greeting3 ations
|
||||||
|
|
||||||
|
|
||||||
|
#semicolon also delimits commands
|
||||||
|
set greeting1 Sal; set greeting2 ut; set greeting3 ations
|
||||||
|
|
||||||
|
|
||||||
|
# Dollar sign introduces variable substitution
|
||||||
|
set greeting $greeting1$greeting2$greeting3
|
||||||
|
|
||||||
|
|
||||||
|
# Bracket introduces command substitution. The result of the command is
|
||||||
|
# substituted in place of the bracketed script. When the "set" command is
|
||||||
|
# given only the name of a variable, it returns the value of that variable.
|
||||||
|
set greeting $greeting1$greeting2[set greeting3]
|
||||||
|
|
||||||
|
|
||||||
|
# Command substitution should really be called script substitution, because an
|
||||||
|
# entire script, not just a command, can be placed between the brackets. The
|
||||||
|
# "incr" command increments the value of a variable and returns its value.
|
||||||
|
set greeting $greeting[
|
||||||
|
incr i
|
||||||
|
incr i
|
||||||
|
incr i
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# backslash suppresses the special meaning of characters
|
||||||
|
set amount \$16.42
|
||||||
|
|
||||||
|
|
||||||
|
# backslash adds special meaning to certain characters
|
||||||
|
puts lots\nof\n\n\n\n\n\nnewlines
|
||||||
|
|
||||||
|
|
||||||
|
# A word enclosed in braces is not subject to any special interpretation or
|
||||||
|
# substitutions, except that a backslash before a brace is not counted when look#ing for the closing brace
|
||||||
|
set somevar {
|
||||||
|
This is a literal $ sign, and this \} escaped
|
||||||
|
brace remains uninterpreted
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# In a word enclosed in double quotes, whitespace characters lose their special
|
||||||
|
# meaning
|
||||||
|
set name Neo
|
||||||
|
set greeting "Hello, $name"
|
||||||
|
|
||||||
|
|
||||||
|
#variable names can be any string
|
||||||
|
set {first name} New
|
||||||
|
|
||||||
|
|
||||||
|
# The brace form of variable substitution handles more complex variable names
|
||||||
|
set greeting "Hello, ${first name}"
|
||||||
|
|
||||||
|
|
||||||
|
# The "set" command can always be used instead of variable substitution
|
||||||
|
set greeting "Hello, [set {first name}]"
|
||||||
|
|
||||||
|
|
||||||
|
# To promote the words within a word to individual words of the current
|
||||||
|
# command, use the expansion operator, "{*}".
|
||||||
|
set {*}{name Neo}
|
||||||
|
|
||||||
|
# is equivalent to
|
||||||
|
set name Neo
|
||||||
|
|
||||||
|
|
||||||
|
# An array is a special variable that is a container for other variables.
|
||||||
|
set person(name) Neo
|
||||||
|
set person(gender) male
|
||||||
|
set greeting "Hello, $person(name)"
|
||||||
|
|
||||||
|
|
||||||
|
# A namespace holds commands and variables
|
||||||
|
namespace eval people {
|
||||||
|
namespace eval person1 {
|
||||||
|
set name Neo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#The full name of a variable includes its enclosing namespace(s), delimited by two colons:
|
||||||
|
set greeting "Hello $people::person::name"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## 3. A Few Notes
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# All other functionality is implemented via commands. From this point on,
|
||||||
|
# there is no new syntax. Everything else there is to learn about Tcl is about
|
||||||
|
# the behaviour of individual commands, and what meaning they assign to their
|
||||||
|
# arguments.
|
||||||
|
|
||||||
|
|
||||||
|
# To end up with an interpreter that can do nothing, delete the global
|
||||||
|
# namespace. It's not very useful to do such a thing, but it illustrates the
|
||||||
|
# nature of Tcl.
|
||||||
|
namespace delete ::
|
||||||
|
|
||||||
|
|
||||||
|
# Because of name resolution behaviour, its safer to use the "variable" command to declare or to assign a value to a namespace.
|
||||||
|
namespace eval people {
|
||||||
|
namespace eval person1 {
|
||||||
|
variable name Neo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The full name of a variable can always be used, if desired.
|
||||||
|
set people::person1::name Neo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## 4. Commands
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Math can be done with the "expr" command.
|
||||||
|
set a 3
|
||||||
|
set b 4
|
||||||
|
set c [expr {$a + $b}]
|
||||||
|
|
||||||
|
# Since "expr" performs variable substitution on its own, brace the expression
|
||||||
|
# to prevent Tcl from performing variable substitution first. See
|
||||||
|
# "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions" for details.
|
||||||
|
|
||||||
|
|
||||||
|
# The "expr" command understands variable and command substitution
|
||||||
|
set c [expr {$a + [set b]}]
|
||||||
|
|
||||||
|
|
||||||
|
# The "expr" command provides a set of mathematical functions
|
||||||
|
set c [expr {pow($a,$b)}]
|
||||||
|
|
||||||
|
|
||||||
|
# Mathematical operators are available as commands in the ::tcl::mathop
|
||||||
|
# namespace
|
||||||
|
::tcl::mathop::+ 5 3
|
||||||
|
|
||||||
|
# Commands can be imported from other namespaces
|
||||||
|
namespace import ::tcl::mathop::+
|
||||||
|
set result [+ 5 3]
|
||||||
|
|
||||||
|
|
||||||
|
# New commands can be created via the "proc" command.
|
||||||
|
proc greet name {
|
||||||
|
return "Hello, $name!"
|
||||||
|
}
|
||||||
|
|
||||||
|
#multiple parameters can be specified
|
||||||
|
proc greet {greeting name} {
|
||||||
|
return "$greeting, $name!"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# As noted earlier, braces do not construct a code block. Every value, even
|
||||||
|
# the third argument of the "proc" command, is a string. The previous command
|
||||||
|
# rewritten to not use braces at all:
|
||||||
|
proc greet greeting\ name return\ \"Hello,\ \$name!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# When the last parameter is the literal value, "args", it collects all extra
|
||||||
|
# arguments when the command is invoked
|
||||||
|
proc fold {cmd args} {
|
||||||
|
set res 0
|
||||||
|
foreach arg $args {
|
||||||
|
set res [cmd $res $arg]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fold ::tcl::mathop::* 5 3 3 ;# -> 45
|
||||||
|
|
||||||
|
|
||||||
|
# Conditional execution is implemented as a command
|
||||||
|
if {3 > 4} {
|
||||||
|
puts {This will never happen}
|
||||||
|
} elseif {4 > 4} {
|
||||||
|
puts {This will also never happen}
|
||||||
|
} else {
|
||||||
|
puts {This will always happen}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Loops are implemented as commands. The first, second, and third
|
||||||
|
# arguments of the "for" command are treated as mathematical expressions
|
||||||
|
for {set i 0} {$i < 10} {incr i} {
|
||||||
|
set res [expr {$res + $i}]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The first argument of the "while" command is also treated as a mathematical
|
||||||
|
# expression
|
||||||
|
set i 0
|
||||||
|
while {$i < 10} {
|
||||||
|
incr i 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# A list is a specially-formatted string. In the simple case, whitespace is sufficient to delimit values
|
||||||
|
set amounts 10\ 33\ 18
|
||||||
|
set amount [lindex $amounts 1]
|
||||||
|
|
||||||
|
|
||||||
|
# Braces and backslash can be used to format more complex values in a list. A
|
||||||
|
# list looks exactly like a script, except that the newline character and the
|
||||||
|
# semicolon character lose their special meanings. This feature makes Tcl
|
||||||
|
# homoiconic. There are three items in the following list.
|
||||||
|
set values {
|
||||||
|
|
||||||
|
one\ two
|
||||||
|
|
||||||
|
{three four}
|
||||||
|
|
||||||
|
five\{six
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Since a list is a string, string operations could be performed on it, at the
|
||||||
|
# risk of corrupting the formatting of the list.
|
||||||
|
set values {one two three four}
|
||||||
|
set values [string map {two \{} $values] ;# $values is no-longer a \
|
||||||
|
properly-formatted listwell-formed list
|
||||||
|
|
||||||
|
|
||||||
|
# The sure-fire way to get a properly-formmated list is to use "list" commands
|
||||||
|
set values [list one \{ three four]
|
||||||
|
lappend values { } ;# add a single space as an item in the list
|
||||||
|
|
||||||
|
|
||||||
|
# Use "eval" to evaluate a value as a script
|
||||||
|
eval {
|
||||||
|
set name Neo
|
||||||
|
set greeting "Hello, $name"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# A list can always be passed to "eval" as a script composed of a single
|
||||||
|
# command.
|
||||||
|
eval {set name Neo}
|
||||||
|
eval [list set greeting "Hello, $name"]
|
||||||
|
|
||||||
|
|
||||||
|
# Therefore, when using "eval", use [list] to build up a desired command
|
||||||
|
set command {set name}
|
||||||
|
lappend command {Archibald Sorbisol}
|
||||||
|
eval $command
|
||||||
|
|
||||||
|
|
||||||
|
# A common mistake is not to use list functions when building up a command
|
||||||
|
set command {set name}
|
||||||
|
append command { Archibald Sorbisol}
|
||||||
|
eval $command ;# There is an error here, because there are too many arguments \
|
||||||
|
to "set" in {set name Archibald Sorbisol}
|
||||||
|
|
||||||
|
|
||||||
|
# This mistake can easily occur with the "subst" command.
|
||||||
|
set replacement {Archibald Sorbisol}
|
||||||
|
set command {set name $replacement}
|
||||||
|
set command [subst $command]
|
||||||
|
eval $command ;# The same error as before: to many arguments to "set" in \
|
||||||
|
{set name Archibald Sorbisol}
|
||||||
|
|
||||||
|
|
||||||
|
# The proper way is to format the substituted value using use the "list"
|
||||||
|
# command.
|
||||||
|
set replacement [list {Archibald Sorbisol}]
|
||||||
|
set command {set name $replacement}
|
||||||
|
set command [subst $command]
|
||||||
|
eval $command
|
||||||
|
|
||||||
|
|
||||||
|
# It is extremely common to see the "list" command being used to properly
|
||||||
|
# format values that are substituted into Tcl script templates. There are
|
||||||
|
# several examples of this, below.
|
||||||
|
|
||||||
|
|
||||||
|
# The "apply" command evaluates a string as a command.
|
||||||
|
set cmd {{greeting name} {
|
||||||
|
return "$greeting, $name!"
|
||||||
|
}}
|
||||||
|
apply $cmd Whaddup Neo
|
||||||
|
|
||||||
|
|
||||||
|
# The "uplevel" command evaluates a script in some enclosing scope.
|
||||||
|
proc greet {} {
|
||||||
|
uplevel {puts "$greeting, $name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc set_double {varname value} {
|
||||||
|
if {[string is double $value]} {
|
||||||
|
uplevel [list variable $varname $value]
|
||||||
|
} else {
|
||||||
|
error [list {not a double} $value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The "upvar" command links a variable in the current scope to a variable in
|
||||||
|
# some enclosing scope
|
||||||
|
proc set_double {varname value} {
|
||||||
|
if {[string is double $value]} {
|
||||||
|
upvar 1 $varname var
|
||||||
|
set var $value
|
||||||
|
} else {
|
||||||
|
error [list {not a double} $value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#get rid of the built-in "while" command.
|
||||||
|
rename ::while {}
|
||||||
|
|
||||||
|
|
||||||
|
# Define a new while command with the "proc" command. More sophisticated error
|
||||||
|
# handling is left as an exercise.
|
||||||
|
proc while {condition script} {
|
||||||
|
if {[uplevel 1 [list expr $condition]]} {
|
||||||
|
uplevel 1 $script
|
||||||
|
tailcall [namespace which while] $condition $script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The "coroutine" command creates a separate call stack, along with a command
|
||||||
|
# to enter that call stack. The "yield" command suspends execution in that
|
||||||
|
# stack.
|
||||||
|
proc countdown {} {
|
||||||
|
#send something back to the initial "coroutine" command
|
||||||
|
yield
|
||||||
|
|
||||||
|
set count 3
|
||||||
|
while {$count > 1} {
|
||||||
|
yield [incr count -1]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
coroutine countdown1 countdown
|
||||||
|
coroutine countdown2 countdown
|
||||||
|
puts [countdown 1] ;# -> 2
|
||||||
|
puts [countdown 2] ;# -> 2
|
||||||
|
puts [countdown 1] ;# -> 1
|
||||||
|
puts [countdown 1] ;# -> 0
|
||||||
|
puts [coundown 1] ;# -> invalid command name "countdown1"
|
||||||
|
puts [countdown 2] ;# -> 1
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
[Official Tcl Documentation](http://www.tcl.tk/man/tcl/)
|
||||||
|
|
||||||
|
[Tcl Wiki](http://wiki.tcl.tk)
|
||||||
|
|
||||||
|
[Tcl Subreddit](http://www.reddit.com/r/Tcl)
|
@@ -63,8 +63,8 @@ foo = anUnknownVariable -- 现在 foo = nil.
|
|||||||
|
|
||||||
aBoolValue = false
|
aBoolValue = false
|
||||||
|
|
||||||
--只有nil和false为假; 0和 ''都均为真!
|
--只有nil和false为假; 0和 ''均为真!
|
||||||
if not aBoolValue then print('twas false') end
|
if not aBoolValue then print('false') end
|
||||||
|
|
||||||
-- 'or'和 'and'短路
|
-- 'or'和 'and'短路
|
||||||
-- 类似于C/js里的 a?b:c 操作符:
|
-- 类似于C/js里的 a?b:c 操作符:
|
||||||
@@ -149,7 +149,7 @@ print {} -- 一样可以工作。
|
|||||||
-- Table = Lua唯一的组合数据结构;
|
-- Table = Lua唯一的组合数据结构;
|
||||||
-- 它们是关联数组。
|
-- 它们是关联数组。
|
||||||
-- 类似于PHP的数组或者js的对象,
|
-- 类似于PHP的数组或者js的对象,
|
||||||
-- 它们是哈希表或者字典,也可以当初列表使用。
|
-- 它们是哈希表或者字典,也可以当列表使用。
|
||||||
|
|
||||||
-- 按字典/map的方式使用Table:
|
-- 按字典/map的方式使用Table:
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user