mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-29 12:18:51 +00:00
commit
922fc494bc
@ -72,7 +72,7 @@ void func(); // function which may accept any number of arguments
|
||||
int* ip = nullptr;
|
||||
|
||||
// C standard headers are available in C++.
|
||||
// C headers end in .h, while
|
||||
// C headers end in .h, while
|
||||
// C++ headers are prefixed with "c" and have no ".h" suffix.
|
||||
|
||||
// The C++ standard version:
|
||||
@ -553,10 +553,14 @@ Point Point::operator+(const Point& rhs) const
|
||||
return Point(x + rhs.x, y + rhs.y);
|
||||
}
|
||||
|
||||
// It's good practice to return a reference to the leftmost variable of
|
||||
// an assignment. `(a += b) == c` will work this way.
|
||||
Point& Point::operator+=(const Point& rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
|
||||
// `this` is a pointer to the object, on which a method is called.
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -757,7 +761,7 @@ failure:
|
||||
// things are a little cleaner, but still sub-optimal.
|
||||
void doSomethingWithAFile(const char* filename)
|
||||
{
|
||||
FILE* fh = fopen(filename, "r"); // Open the file in read mode
|
||||
FILE* fh = fopen(filename, "r"); // Open the file in shared_ptrread mode
|
||||
if (fh == nullptr)
|
||||
throw std::runtime_error("Could not open the file.");
|
||||
|
||||
@ -810,6 +814,57 @@ void doSomethingWithAFile(const std::string& filename)
|
||||
// - Mutexes using lock_guard and unique_lock
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Smart Pointer
|
||||
/////////////////////
|
||||
|
||||
// Generally a smart pointer is a class, which wraps a "raw pointer" (usage of "new"
|
||||
// respectively malloc/calloc in C). The goal is to be able to
|
||||
// manage the lifetime of the object being point to without explicitly deleting
|
||||
// the object. The term itself simply describes a set of pointers with the
|
||||
// mentioned abstraction.
|
||||
// Basically smart pointers should preferred over raw pointers, to prevent
|
||||
// risky memory leaks, which happens if you forget to delete the object.
|
||||
|
||||
// Usage of a raw pointer:
|
||||
Dog* ptr = new Dog();
|
||||
ptr->bark();
|
||||
delete ptr;
|
||||
|
||||
// With the usage of smart pointers you dont have to worry about the deletion
|
||||
// of a object anymore.
|
||||
// A smart pointer describes a policy, to count the references on the
|
||||
// pointer. As matter of fact the objects gets destroyed when the last
|
||||
// reference on the object gets destroyed.
|
||||
|
||||
// Usage of "std::shared_ptr":
|
||||
void foo()
|
||||
{
|
||||
// Its not longer necessary to delete the Dog.
|
||||
std::shared_ptr<Dog> doggo(new Dog());
|
||||
doggo->bark();
|
||||
}
|
||||
|
||||
// Beware of possible circular references!!!
|
||||
// There will be always a reference, so it will be never destroyed!
|
||||
std::shared_ptr<Dog> doggo_one (new Dog());
|
||||
std::shared_ptr<Dog> doggo_two (new Dog());
|
||||
doggo_one = doggo_two; // p1 references p2
|
||||
doggo_two = doggo_one; // p2 references p1
|
||||
|
||||
// As mentioned before there is a set of smart pointers. The way you have to
|
||||
// use it, is always the same.
|
||||
// This leads us to question, when to use which one?
|
||||
// std::unique_ptr - use it when you just want to hold one reference on
|
||||
// the same object.
|
||||
// std::shared_ptr - use it when you want to hold multiple references on the
|
||||
// same object and want to make sure that it´s de-allocated
|
||||
// when all refences are gone.
|
||||
// std::weak_ptr - use it when you want to hold multiple references from
|
||||
// different places for references for which it´s no problem
|
||||
// tp de-allocate.
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Containers
|
||||
/////////////////////
|
||||
@ -873,7 +928,7 @@ cout << ST.size(); // will print the size of set ST
|
||||
// Output: 0
|
||||
|
||||
// NOTE: for duplicate elements we can use multiset
|
||||
// NOTE: For hash sets, use unordered_set. They are more efficient but
|
||||
// NOTE: For hash sets, use unordered_set. They are more efficient but
|
||||
// do not preserve order. unordered_set is available since C++11
|
||||
|
||||
// Map
|
||||
@ -902,7 +957,7 @@ cout << it->second;
|
||||
|
||||
// Output: 26
|
||||
|
||||
// NOTE: For hash maps, use unordered_map. They are more efficient but do
|
||||
// NOTE: For hash maps, use unordered_map. They are more efficient but do
|
||||
// not preserve order. unordered_map is available since C++11.
|
||||
|
||||
// Containers with object keys of non-primitive values (custom classes) require
|
||||
|
@ -23,9 +23,11 @@ entworfen wurde um,
|
||||
- generische Programmierung zu unterstützen
|
||||
|
||||
Durch seinen Syntax kann sie durchaus schwieriger und komplexer als neuere Sprachen sein.
|
||||
Sie ist weit verbeitet, weil sie in Maschinen-Code compiliert, welches direkt vom Prozessor ausgeführt
|
||||
|
||||
Sie ist weit verbreitet, weil sie in Maschinen-Code kompiliert, welches direkt vom Prozessor ausgeführt
|
||||
werden kann und somit eine strikte Kontrolle über die Hardware bietet und gleichzeitig
|
||||
High-Level-Features wie generics, exceptions und Klassen enthält. (wie C)
|
||||
High-Level-Features wie generics, exceptions und Klassen enthält.
|
||||
|
||||
Diese Kombination aus Geschwindigkeit und Funktionalität bildet C++ und ist eine der
|
||||
weitverbreitesten Programmiersprachen.
|
||||
|
||||
@ -34,19 +36,20 @@ weitverbreitesten Programmiersprachen.
|
||||
// Vergleich zu C
|
||||
//////////////////
|
||||
|
||||
// C++ ist fast eine Untermenge von C and teilt sich grundsätzlich den
|
||||
// Syntax für Variablen Deklarationen, primitiven Typen und Funktionen.
|
||||
// C ist fast eine Untermenge von C++ und teilt sich grundsätzlich den
|
||||
// Syntax für Variablen Deklarationen, primitiven Typen und Funktionen.
|
||||
|
||||
// Wie in C ist der Programmeinsprungpunkt eine Funktion, welche "main" genannt wird und
|
||||
// einen Ineteger als Rückgabetyp besitzt.
|
||||
// einen Integer als Rückgabetyp besitzt.
|
||||
|
||||
// Dieser Wert fungiert als Beendigungsstatus des Programms.
|
||||
// Siehe: https://de.wikipedia.org/wiki/Return_Code für weitere Informationen
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Kommandozeilen Argumente werden genauso wie in C über argc und argv übergeben
|
||||
// argc entspricht der Anzahl von Argumenten und argv ist ein Array von C-style
|
||||
// argc entspricht der Anzahl von Argumenten und argv ist ein Array von C-style
|
||||
// strings (char*), welche die Argumente repräsentieren.
|
||||
// Das erste Argument ist der Name des Programms welches aufgerufen wird.
|
||||
// Das erste Argument ist der Name des Programms, welches aufgerufen wird.
|
||||
// Argc und argv können, wenn nicht benötigt, weg gelassen werden, indem
|
||||
// die Funktionssignatur "int main()" verwendet wird.
|
||||
|
||||
@ -54,12 +57,12 @@ int main(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// C++ unterscheidet sich in einigen Punkten:
|
||||
// C++ unterscheidet sich in einigen Punkten von C:
|
||||
|
||||
// In C++ sind Zeichen-Literale chars
|
||||
// In C++ sind Zeichen-Literale char´s
|
||||
sizeof('c') == sizeof(char) == 1
|
||||
|
||||
// In C sind Zeichen-Literale ints
|
||||
// In C sind Zeichen-Literale int´s
|
||||
sizeof('c') == sizeof(int)
|
||||
|
||||
// C++ verwendet striktes prototyping
|
||||
@ -71,8 +74,8 @@ void func(); // Funktion mit beliebiger Anzahl von Argumenten
|
||||
// Verwende nullptr, anstatt von NULL!!!
|
||||
int* ip = nullptr;
|
||||
|
||||
// C standard headers sind in C++ verfügbar.
|
||||
// C header enden mit .h, während
|
||||
// C standard header sind in C++ verfügbar.
|
||||
// C header enden mit .h, während
|
||||
// C++ header das Präfix "c" besitzen und kein ".h" Suffix verwenden.
|
||||
|
||||
// Die C++ Standard Version:
|
||||
@ -115,10 +118,9 @@ int main()
|
||||
|
||||
// Argumente können per Standard für eine Funktion gesetzt werden,
|
||||
// wenn diese beim Aufruf nicht bereitgestellt werden.
|
||||
|
||||
void doSomethingWithInts(int a = 1, int b = 4)
|
||||
{
|
||||
// führe Anweisungen mit "ints" aus.
|
||||
// führe Anweisungen mit "int´s" aus.
|
||||
}
|
||||
|
||||
int main()
|
||||
@ -141,18 +143,18 @@ void invalidDeclaration(int a = 1, int b) // Fehler!
|
||||
// Namespaces stellen einen getrennten Gültigkeitsbereich für Variablen,
|
||||
// Funktionen und andere Deklarationen zur Verfügung.
|
||||
// Namespaces können geschachtelt werden.
|
||||
namespace First
|
||||
namespace First
|
||||
{
|
||||
namespace Nested
|
||||
namespace Nested
|
||||
{
|
||||
void foo()
|
||||
{
|
||||
printf("This is First::Nested::foo\n");
|
||||
}
|
||||
} // Ende des Namespaces "Nested"
|
||||
} // Ende des Namespaces "First"
|
||||
} // Ende des Namespace "Nested"
|
||||
} // Ende des Namespace "First"
|
||||
|
||||
namespace Second
|
||||
namespace Second
|
||||
{
|
||||
void foo()
|
||||
{
|
||||
@ -236,8 +238,8 @@ cout << myString; // "Hello Dog"
|
||||
// C++ besitzt _Referenzen_.
|
||||
// Diese sind Pointer-Typen, welche nicht erneut zugewiesen werden können
|
||||
// und nicht Null sein können.
|
||||
// Sie besitzen den selben Synthax wie Variablen.
|
||||
// Für die Dereferenzierung ist kein * notwendig und
|
||||
// Sie besitzen den selben Syntax wie Variablen.
|
||||
// Für die Dereferenzierung ist kein * notwendig und
|
||||
// & (die Adresse) wird nicht für die Zuweisung verwendet.
|
||||
|
||||
using namespace std;
|
||||
@ -261,19 +263,18 @@ cout << fooRef; // Gibt "I am bar" aus
|
||||
|
||||
// Die Adresse von fooRef verbleibt die selbe, sie verweist immer noch auf foo
|
||||
|
||||
|
||||
const string& barRef = bar; // Erzeugt konstante Referenz auf bar.
|
||||
// Wie in C, können konstante Werte ( und Pointer bzw. Referenzen) nicht verändert werden.
|
||||
|
||||
barRef += ". Hi!"; // Fehler: konstante Referenzen können nicht verändert werden.
|
||||
|
||||
// Hinweis: bevor wir genauer Referenzen besprechen, schauen wir uns zuerst ein Konzept an
|
||||
// Hinweis: bevor wir genauer Referenzen besprechen, schauen wir uns zuerst ein Konzept an,
|
||||
// welches als "temporäres Objekt" bezeichnet wird. Gehen wir von folgenden Code aus:
|
||||
string tempObjectFun() { ... }
|
||||
string retVal = tempObjectFun();
|
||||
|
||||
// Was passiert nun in der zweiten Zeile:
|
||||
// - ein String Objekt wird von tempObjectFun zurückgegeben
|
||||
// - ein String Objekt wird von "tempObjectFun" zurückgegeben
|
||||
// - ein neuer String wird mit dem zurückgegebenen Objekt als Argument für den Konstruktor erzeugt.
|
||||
// - das zurückgegebene Objekt wird zerstört
|
||||
// Das zurückgegbene Objekt wird temporäres Objekt genannt. Temporäre Objekte werden erzeugt
|
||||
@ -285,19 +286,20 @@ foo(bar(tempObjectFun()))
|
||||
// Nehmen wir an foo und bar existieren. Das Objekt wird von "tempObjectFun" zurückgegeben,
|
||||
// wird an bar übergeben und ist zerstört bevor foo aufgerufen wird.
|
||||
|
||||
// Zurück zu Referenzen. Die Ausnahme, dass die "am Ende des Ausdrucks" Regel ist gültig,
|
||||
// wenn das temporäre Objekt an eine konstante Referenz gebunden ist, in welchem Fall das
|
||||
// Leben auf den aktuellen Gültigkeitsbereich erweitert wird.
|
||||
// Zurück zu Referenzen. Die Annahme, dass die "am Ende des Ausdrucks" Regel gültig ist,
|
||||
// wenn das temporäre Objekt an eine konstante Referenz gebunden ist, ist der Fall, wenn die Lebensdauer
|
||||
// auf den aktuellen Gültigkeitsbereich erweitert wird.
|
||||
|
||||
void constReferenceTempObjectFun() {
|
||||
// constRef erhält das temporäre Objekt und ist gültig bis ans Ende der Funktion
|
||||
// constRef erhält das temporäre Objekt und ist gültig bis ans Ende der Funktion
|
||||
const string& constRef = tempObjectFun();
|
||||
...
|
||||
}
|
||||
|
||||
// Eine andere Art von Referenzen wird in C++11 eingeführt und ist speziell für
|
||||
// temporäre Objekte. Es ist nicht möglich Variablen des Typs zu besitzen, aber
|
||||
// Vorrechte bei der Auflösung.
|
||||
// Eine andere Art von Referenzen wurde in C++11 eingeführt und ist speziell für
|
||||
// temporäre Objekte. Es ist nicht möglich Variablen des Typs zu besitzen, aber
|
||||
// Vorrechte bei der Auflösung zu besitzen.
|
||||
|
||||
void someFun(string& s) { ... } // Reguläre Referenz
|
||||
void someFun(string&& s) { ... } // Referenz auf ein temporäres Objekt
|
||||
|
||||
@ -310,8 +312,8 @@ someFun(tempObjectFun()); // Ruft die Funktion mit der temporären Referenz auf
|
||||
basic_string(const basic_string& other);
|
||||
basic_string(basic_string&& other);
|
||||
|
||||
// Nehmen wir an, wir erzeugen einen neuen String eines temporären Objekts (welches später
|
||||
// zerstört wird), hierbei existiert ein effizienterer Konstruktor. Dieses Konzept wird
|
||||
// Nehmen wir an, wir erzeugen einen neuen String eines temporären Objekts (welches später
|
||||
// zerstört wird), hierbei existiert ein effizienterer Konstruktor. Dieses Konzept wird
|
||||
// als "move semantics" bezeichnet (bewegen eines Objekts in ein anderes in C++).
|
||||
|
||||
/////////////////////
|
||||
@ -346,18 +348,18 @@ enum ECarTypes : uint8_t
|
||||
|
||||
void WriteByteToFile(uint8_t InputValue)
|
||||
{
|
||||
// Serialisierung von InputValue in eine Datei
|
||||
// Serialisierung von "InputValue" in eine Datei
|
||||
}
|
||||
|
||||
void WritePreferredCarTypeToFile(ECarTypes InputCarType)
|
||||
{
|
||||
// Das enum wird implizit zu einem "uint8_t" konvertiert. Bedingt dadurch, dass
|
||||
// es sich um ein enum handelt.
|
||||
// es sich um ein "enum" handelt.
|
||||
WriteByteToFile(InputCarType);
|
||||
}
|
||||
|
||||
// Nicht immer ist es gewünscht, dass enums zu einem Integer oder zu einem anderen
|
||||
// enum umgewandelt werden. Daher ist es möglich eine enum-Klasse zu erzeugen, welche
|
||||
// Nicht immer ist es gewünscht, dass enum´s zu einem Integer oder zu einem anderen
|
||||
// enum umgewandelt werden. Daher ist es möglich eine enum-Klasse zu erzeugen, welche
|
||||
// nicht implizit umgewandelt wird.
|
||||
enum class ECarTypes : uint8_t
|
||||
{
|
||||
@ -374,8 +376,8 @@ void WriteByteToFile(uint8_t InputValue)
|
||||
|
||||
void WritePreferredCarTypeToFile(ECarTypes InputCarType)
|
||||
{
|
||||
// Wird nicht kompilieren, da ECarTypes ein "uint8_t" ist, da das enum
|
||||
// als "enum class" deklariert wurde!
|
||||
// Wird nicht kompilieren, da "ECarTypes" ein "uint8_t" ist, da das enum
|
||||
// als "enum class" deklariert wurde!
|
||||
WriteByteToFile(InputCarType);
|
||||
}
|
||||
|
||||
@ -401,15 +403,15 @@ public:
|
||||
// Standard Konstruktor
|
||||
Dog();
|
||||
|
||||
// Member-Funktonensdeklaration (Implementierung folgt)
|
||||
// Member-Funktionsdeklaration (Implementierung folgt).
|
||||
// Bemerkung: std::string statt der Verwendung von namespace std;
|
||||
// "using namespace" sollte niemals in einem header verwendet werden.
|
||||
void setName(const std::string& dogsName);
|
||||
|
||||
void setWeight(int dogsWeight);
|
||||
|
||||
// Funktionen, die Objekte nicht ändern sollte mit const deklariert werden.
|
||||
// Funktionen müssen explizit als "virtual" deklariert werden, um in einer
|
||||
// Funktionen, die Objekte nicht ändern, sollten mit const deklariert werden.
|
||||
// Funktionen müssen explizit als "virtual" deklariert werden, um in einer
|
||||
// abgeleiteten Klassen überschrieben zu werden.
|
||||
// Aus performance Gründen sind Funktionen nicht per default virtual.
|
||||
virtual void print() const;
|
||||
@ -419,24 +421,24 @@ public:
|
||||
void bark() const { std::cout << name << " barks!\n"; }
|
||||
|
||||
// Neben Konstruktoren, bietet C++ Destruktoren.
|
||||
// Diese werden aufgerufen, wenn ein Objekt freigegeben wird oder
|
||||
// seinen Wertebereich verlässt.
|
||||
// Diese werden aufgerufen, wenn ein Objekt freigegeben wird oder
|
||||
// seinen Wertebereich verlässt.
|
||||
// Dies ermöglicht mächtige Paradigmen, wie auch RAII.
|
||||
// Destruktoren sollten virtual sein, wenn eine Klasse von ihr
|
||||
// abgeleitet wird. Ist dieser nicht virtual, dann wird der
|
||||
// Destruktor der abgeleiteten Klasse nicht aufgerufen, insofern
|
||||
// Destruktor der abgeleiteten Klasse nicht aufgerufen, insofern
|
||||
// das Objekt durch eine Referenz/Pointer der Basisklasse entfernt wird.
|
||||
virtual ~Dog();
|
||||
|
||||
}; // Ein Semikolon schließt die Definition der Klasse ab.
|
||||
|
||||
// Klassen-Member-Funktionen sind üblicherweise in der .cpp Datei implmentiert.
|
||||
// Klassen-Member-Funktionen sind üblicherweise in der .cpp Datei implementiert.
|
||||
Dog::Dog()
|
||||
{
|
||||
std::cout << "A dog has been constructed\n";
|
||||
}
|
||||
|
||||
// Objekte sollten als Referenz übergeben werden und wenn diese nicht
|
||||
// Objekte sollten als Referenz übergeben werden und wenn diese nicht
|
||||
// verändert werden sollen, sollte das Objekt als const Referenz übergeben werden.
|
||||
void Dog::setName(const std::string& dogsName)
|
||||
{
|
||||
@ -468,8 +470,6 @@ int main()
|
||||
return 0;
|
||||
} // Ausgabe: "Goodbye Barkley"
|
||||
|
||||
// Vererbung:
|
||||
|
||||
// Diese Klasse erbt alles was public bzw. protected ist von der Dog-Klasse
|
||||
// und darüber hinaus auch private Methoden/Attribute, jedoch kann auf diese
|
||||
// nicht direkt zugegriffen werden. Lediglich über public/procted getter/setter.
|
||||
@ -481,7 +481,7 @@ public:
|
||||
// Überschreibt das Verhalten der "print" Funktion für alle "OwnedDogs".
|
||||
// Siehe: http://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping
|
||||
// für eine grundlegende Einführung in "Subtype Polymorphismus".
|
||||
// Das "override" Schlüsselwort ist optional, aber stellt sicher, dass die
|
||||
// Das "override" Schlüsselwort ist optional, aber stellt sicher, dass die
|
||||
// Methode der Basisklasse tatsächlich überschrieben wurde.
|
||||
void print() const override;
|
||||
|
||||
@ -555,7 +555,7 @@ Point& Point::operator+=(const Point& rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
int main ()
|
||||
int main ()
|
||||
{
|
||||
Point up (0,1);
|
||||
Point right (1,0);
|
||||
@ -574,23 +574,23 @@ int main ()
|
||||
// Templates in C++ werden in erster Linie dafür verwendet generisch zu programmieren.
|
||||
// Sie unterstützen explizite und partielle Spezialisierung und darüber hinaus können
|
||||
// sie für funktionale Klassen verwendet werden.
|
||||
// Tatsächlich bilden templates die Turing-Vollständigkeit
|
||||
// Tatsächlich bilden templates die Turing-Vollständigkeit
|
||||
// (universelle Programmierbarkeit) ab.
|
||||
|
||||
|
||||
// Zu Beginn ein einführendes Beispiel der generischen Programmierung.
|
||||
// Die Definition einer Klasse bzw. Funktion, welche mit dem Typ T parametriert wird.
|
||||
template<class T>
|
||||
class Box
|
||||
class Box
|
||||
{
|
||||
public:
|
||||
// T repräsentiert an dieser Stelle einen beliebigen Typen.
|
||||
void insert(const T&) { ... }
|
||||
};
|
||||
|
||||
// Während der Kompilierung generiert der Kompiler Kopien für jedes Template, wobei
|
||||
// hierbei die Parameter substituiert werden. Somit muss bei jedem Aufruf die gesamte
|
||||
// Definition der Klasse zur Verfügung stehen. Aus diesem Grund wird ein Template
|
||||
// Während der Kompilierung generiert der Compiler Kopien für jedes template, wobei
|
||||
// hierbei die Parameter substituiert werden. Somit muss bei jedem Aufruf die gesamte
|
||||
// Definition der Klasse zur Verfügung stehen. Aus diesem Grund wird ein Template
|
||||
// komplett im header definiert.
|
||||
|
||||
// Erzeugung einer Template-Klasse auf dem stack:
|
||||
@ -609,7 +609,7 @@ boxOfBox.insert(intBox);
|
||||
// Manchmal ist folgende Notation anzutreffen:
|
||||
// template<typename T>
|
||||
// Das 'class' Schlüsselwort und das 'typename' Schlüsselwort
|
||||
// sind fast identisch hinsichtlich der Funktionalität. Weitere
|
||||
// sind fast identisch hinsichtlich der Funktionalität. Weitere
|
||||
// Informationen auf: http://en.wikipedia.org/wiki/Typename
|
||||
|
||||
// Eine template-Funktion:
|
||||
@ -623,7 +623,7 @@ void barkThreeTimes(const T& input)
|
||||
|
||||
// Hierbei ist zu beachten, dass an dieser Stelle nichts über den Typen des Parameters
|
||||
// definiert wurde. Der Kompiler wird bei jedem Aufruf bzw. jeder Erzeugung den Typen
|
||||
// prüfen. Somit funktioniert die zuvor definiert Funktion für jeden Typ 'T', die die
|
||||
// prüfen. Somit funktioniert die zuvor definierte Funktion für jeden Typ 'T', die die
|
||||
// const Methode 'bark' implementiert hat.
|
||||
|
||||
Dog fluffy;
|
||||
@ -632,14 +632,14 @@ barkThreeTimes(fluffy); // Gibt "Fluffy barks" dreimal aus.
|
||||
|
||||
// Template Parameter müssen keine Klassen sein.
|
||||
template<int Y>
|
||||
void printMessage()
|
||||
void printMessage()
|
||||
{
|
||||
cout << "Learn C++ in " << Y << " minutes!" << endl;
|
||||
}
|
||||
|
||||
// Des Weiteren können templates aus Effizienzgründen genauer spezifiziert werden.
|
||||
// Selbstverständlich sind reale-Problemen, welche genauer spezifiziert werden nicht
|
||||
// derart trivial. Auch wenn alle Parameter explizit definiert wurden, muss die
|
||||
// derart trivial. Auch wenn alle Parameter explizit definiert wurden, muss die
|
||||
// Funktion oder Klasse als template deklariert werden.
|
||||
template<>
|
||||
void printMessage<10>()
|
||||
@ -655,17 +655,17 @@ printMessage<10>(); // Gibt "Learn C++ faster in only 10 minutes!" aus.
|
||||
// Ausnahme Behandlungen (Exception-Handling)
|
||||
/////////////////////
|
||||
|
||||
// Die Standard Bibliothek bietet einige Exceptions.
|
||||
// Die Standard Bibliothek bietet einige exceptions.
|
||||
// Siehe: http://en.cppreference.com/w/cpp/error/exception.
|
||||
// Grundsätzlich können alle Typen als Exception geworfen werden.
|
||||
// Grundsätzlich können alle Typen als exception geworfen werden.
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
// Alle Exceptions, die in dem "try" Block geworfen werden, können mittels
|
||||
// Alle exceptions, die in dem "try" Block geworfen werden, können mittels
|
||||
// "catch" abgefangen werden.
|
||||
try
|
||||
try
|
||||
{
|
||||
// Exceptions sollten nicht auf dem heap mithilfe
|
||||
// exceptions sollten nicht auf dem heap mithilfe
|
||||
// von "new" allokiert werden.
|
||||
throw std::runtime_error("A problem occurred");
|
||||
}
|
||||
@ -690,7 +690,7 @@ catch (...)
|
||||
|
||||
// RAII steht für "Resource Acquisition Is Initialization".
|
||||
// Oft wird dies als eines der wichtigsten Paradigmen in C++ betrachtet.
|
||||
// RAII beschreibt das Konzept, dass der Konstruktor für ein Objekt
|
||||
// RAII beschreibt das Konzept, dass der Konstruktor für ein Objekt
|
||||
// die Ressourcen akquiriert und der Destruktor diese freigibt.
|
||||
|
||||
// Zum Verständnis, warum dies sinnvoll ist, nachfolgend
|
||||
@ -726,7 +726,7 @@ bool doSomethingWithAFile(const char* filename)
|
||||
return false; // Fehler "melden".
|
||||
}
|
||||
|
||||
if (!doSomethingElseWithIt(fh))
|
||||
if (!doSomethingElseWithIt(fh))
|
||||
{
|
||||
fclose(fh); // File handle schließen.
|
||||
return false; // Fehler "melden".
|
||||
@ -770,12 +770,12 @@ void doSomethingWithAFile(const char* filename)
|
||||
doSomethingWithTheFile(fh);
|
||||
doSomethingElseWithIt(fh);
|
||||
}
|
||||
catch (...)
|
||||
catch (...)
|
||||
{
|
||||
// Im Fehlerfall sollte sichergestellt sein, dass die
|
||||
// Im Fehlerfall sollte sichergestellt sein, dass die
|
||||
// Datei geschlossen wird.
|
||||
fclose(fh);
|
||||
throw; // Erneutes werfen der Exception
|
||||
fclose(fh);
|
||||
throw; // Erneutes werfen der exception
|
||||
}
|
||||
|
||||
fclose(fh); // Schließen der Datei
|
||||
@ -796,7 +796,7 @@ void doSomethingWithAFile(const std::string& filename)
|
||||
} // Die Datei wird automatisch vom Destruktor geschlossen.
|
||||
|
||||
// Diese Vorgehensweise bietet massive Vorteile:
|
||||
// 1. Egal was passiert, die Ressource (das Datei-Handle) wird aufgelöst,
|
||||
// 1. Egal was passiert, die Ressource (das Datei-Handle) wird aufgelöst,
|
||||
// insofern der Destruktor korrekt beschrieben wurde. Es ist möglich
|
||||
// zu vergessen das Datei-Handle zu schließen, was zu einem "leak" der
|
||||
// entsprechenden Ressource führt.
|
||||
@ -804,13 +804,13 @@ void doSomethingWithAFile(const std::string& filename)
|
||||
// Der Destruktor wird das Datei-Handle im Hintergrund schließen und der
|
||||
// Programmierer muss sich darum keinerlei Sorgen machen.
|
||||
// 3. Der Code ist "exception sicher".
|
||||
// Egal wo die exception geworfen wird, das Aufäumen wird definitv vollzogen.
|
||||
// Egal wo die exception geworfen wird, das Aufräumen wird definitiv vollzogen.
|
||||
|
||||
// Der gesamte idiomatische C++ Code verwendet RAII für alle Ressourcen.
|
||||
// Weitere Beispiele:
|
||||
// - Speicher verwenden "unique_ptr" und "shared_ptr".
|
||||
// - Container - verkettete Listen (linked list), vector (selbst organisierende
|
||||
// Arrays), hash maps, etc., entfernen deren Inhalt, wenn diese außerhalb des
|
||||
// Arrays), hash maps, etc., entfernen deren Inhalt, wenn diese außerhalb des
|
||||
// Gültigkeitsbereichs laufen.
|
||||
// - Mutex´s verwenden lock_guard und unique_lock.
|
||||
|
||||
@ -818,7 +818,7 @@ void doSomethingWithAFile(const std::string& filename)
|
||||
// Container
|
||||
/////////////////////
|
||||
|
||||
// Die Container der Standard Tenplate Bibliothek beinhaltet einige vordefinierter templates.
|
||||
// Die Container der Standard template Bibliothek beinhaltet einige vordefinierter templates.
|
||||
// Diese verwalten die Speicherbereiche für die eigenen Elemente und stellen Member-Funktionen
|
||||
// für den Zugriff und die Maniplulation bereit.
|
||||
|
||||
@ -842,7 +842,7 @@ for (int i = 0; i < my_vector.size(); i++)
|
||||
|
||||
// Oder die Verwendung von Iteratoren:
|
||||
vector<string>::iterator it; // Initialisierng des Iterators.
|
||||
for (it = my_vector.begin(); it != my_vector.end(); ++it)
|
||||
for (it = my_vector.begin(); it != my_vector.end(); ++it)
|
||||
{
|
||||
cout << *it << endl;
|
||||
}
|
||||
@ -861,13 +861,13 @@ ST.insert(30); // Einfügen des Werts 30 in das Set ST
|
||||
// 10 20 30
|
||||
|
||||
// Entfernen eines Elements:
|
||||
ST.erase(20);
|
||||
ST.erase(20);
|
||||
|
||||
// Set ST: 10 30
|
||||
// Für das iterieren verwenden wir Iteratoren:
|
||||
set<int>::iterator it;
|
||||
|
||||
for(it=ST.begin();it<ST.end();it++)
|
||||
for(it=ST.begin();it<ST.end();it++)
|
||||
{
|
||||
cout << *it << endl;
|
||||
}
|
||||
@ -876,7 +876,7 @@ for(it=ST.begin();it<ST.end();it++)
|
||||
// 10
|
||||
// 30
|
||||
|
||||
// Zum leeren des gesmten Container wird die Methode
|
||||
// Zum leeren des gesamten Container wird die Methode
|
||||
// Container._name.clear() verwendet.
|
||||
ST.clear();
|
||||
cout << ST.size(); // Ausgabe der Set-Größe
|
||||
@ -885,7 +885,7 @@ cout << ST.size(); // Ausgabe der Set-Größe
|
||||
|
||||
// Bemerkung: für mehrdeutige Elemente werden multisets verwendet.
|
||||
// Für hash-Sets sollten unordered_set´s verwendet werden, da diese
|
||||
// wesentlich effizienter sind, allerdings keiner Ordnung folgen.
|
||||
// wesentlich effizienter sind, allerdings keiner Ordnung folgen.
|
||||
// Verfügbar sind diese Features ab C++11.
|
||||
|
||||
// Map
|
||||
@ -899,7 +899,7 @@ mymap.insert(pair<char,int>('A',1)); // Einfügen des Werts "1" für den Key "A"
|
||||
|
||||
mymap.insert(pair<char,int>('Z',26)); // Einfügen des Werts "26" für den Key "Z".
|
||||
|
||||
// Das Iterieren über Maps:
|
||||
// Das Iterieren über Maps:
|
||||
map<char,int>::iterator it;
|
||||
for (it=mymap.begin(); it!=mymap.end(); ++it)
|
||||
std::cout << it->first << "->" << it->second << '\n';
|
||||
@ -918,25 +918,25 @@ cout << it->second;
|
||||
// sind effizienter und benötigen keine Reihenfolge. "unordered_maps" sind ab
|
||||
// C++11 verfügbar.
|
||||
|
||||
// Container für nicht-primitve Datentypen benötigen Vergleichsfunktionen im Objekt selbst,
|
||||
// Container für nicht-primitive Datentypen benötigen Vergleichsfunktionen im Objekt selbst,
|
||||
// oder als Funktionspointer. Primitive Datentypen besitzen default-Vergleichsfunktionen.
|
||||
// Allerdings können diese überschrieben werden.
|
||||
class Foo
|
||||
class Foo
|
||||
{
|
||||
public:
|
||||
int j;
|
||||
Foo(int a) : j(a) {}
|
||||
};
|
||||
|
||||
struct compareFunction
|
||||
struct compareFunction
|
||||
{
|
||||
bool operator()(const Foo& a, const Foo& b) const
|
||||
bool operator()(const Foo& a, const Foo& b) const
|
||||
{
|
||||
return a.j < b.j;
|
||||
}
|
||||
};
|
||||
|
||||
// Folgender Code ist nicht valide, könnte aber von einigen Compilern
|
||||
// Folgender Code ist nicht valide, könnte aber von einigen Compilern
|
||||
// als valide angesehen werden:
|
||||
// std::map<Foo, int> fooMap;
|
||||
std::map<Foo, int, compareFunction> fooMap;
|
||||
@ -949,10 +949,10 @@ fooMap.find(Foo(1)); // Wahr
|
||||
///////////////////////////////////////
|
||||
|
||||
// Lambdas sind eine gängige Methodik um anonyme Funktionen an dem
|
||||
// Ort der Verwendung zu definieren. Darüber hinaus auch bei der
|
||||
// Ort der Verwendung zu definieren. Darüber hinaus auch bei der
|
||||
// Verwendung von Funktionen als Argument einer Funktion.
|
||||
|
||||
// Nehmen wir an es soll ein Vektor von "pairs" (Paaren) mithilfe
|
||||
// Nehmen wir an es soll ein Vektor von "pairs" (Paaren) mithilfe
|
||||
// des zweiten Werts des "pairs" sortiert werden.
|
||||
|
||||
vector<pair<int, int> > tester;
|
||||
@ -968,19 +968,19 @@ sort(tester.begin(), tester.end(), [](const pair<int, int>& lhs, const pair<int,
|
||||
|
||||
// Beachte den Syntax von Lambda-Ausdrücken.
|
||||
// Die [] im Lambda Ausdruck werden für die Variablen verwendet.
|
||||
// Diese so genannte "Capture List" definiert, was außerhalb des Lambdas
|
||||
// Diese so genannte "capture list" definiert, was außerhalb des Lambdas,
|
||||
// innerhalb der Funktion verfügbar sein soll und in welcher Form.
|
||||
// Dies kann folgendes sein:
|
||||
// 1. ein Wert [x]
|
||||
// 2. eine Referenz [&x]
|
||||
// 3. eine beliebige Variable, welche sich im Gültigkeitsbereich durch
|
||||
// 3. eine beliebige Variable, welche sich im Gültigkeitsbereich durch
|
||||
// die Referenz [&] befindet.
|
||||
// 4. wie bei 3. aber mithilfe des Werts [=]
|
||||
// Beispiel:
|
||||
|
||||
vector<int> dog_ids;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
dog_ids.push_back(i);
|
||||
}
|
||||
@ -1013,8 +1013,7 @@ for(int elem: arr)
|
||||
}
|
||||
|
||||
// Insofern "auto" verwendet wird, muss der Typ nicht weiter beachtet werden.
|
||||
|
||||
for(auto elem: arr)
|
||||
for(auto elem: arr)
|
||||
{
|
||||
// Anweisungen ...
|
||||
}
|
||||
@ -1029,18 +1028,17 @@ for(auto elem: arr)
|
||||
// C++ ist eine der Sprachen, bei der es ein leichtes ist sich selbst ins Bein zu schießen.
|
||||
|
||||
// Private-Methoden können überschrieben werden
|
||||
class Foo
|
||||
class Foo
|
||||
{
|
||||
virtual void bar();
|
||||
};
|
||||
|
||||
class FooSub : public Foo
|
||||
class FooSub : public Foo
|
||||
{
|
||||
virtual void bar(); // Überschreibt Foo::bar!
|
||||
};
|
||||
|
||||
|
||||
// 0 == false == NULL
|
||||
// 0 == false == NULL
|
||||
bool* pt = new bool;
|
||||
*pt = 0; // Setzt den Wert des Pointers 'pt' auf false.
|
||||
pt = 0; // Setzt 'pt' auf den "null-pointer". Keine Compiler-Warnung.
|
||||
@ -1050,18 +1048,17 @@ int* pt2 = new int;
|
||||
*pt2 = nullptr; // Kompiliert nicht.
|
||||
pt2 = nullptr; // Setzt pt2 auf null.
|
||||
|
||||
// Eine Ausnahme bilden bools.
|
||||
// Eine Ausnahme bilden bool´s.
|
||||
// Dies erlaubt es "null-pointer" zu testen: if(!ptr)
|
||||
// Die Konsequenz ist jedoch, dass dem nullptr ein bool zugewiesen werden kann.
|
||||
*pt = nullptr; // Kompiliert auch wenn '*pt' ein bool ist!
|
||||
|
||||
*pt = nullptr; // Kompiliert auch, wenn '*pt' ein bool ist!
|
||||
|
||||
// '=' != '=' != '='!
|
||||
// Ruft Foo::Foo(const Foo&) auf, oder den Kopierkonstruktor
|
||||
Foo f2;
|
||||
Foo f1 = f2;
|
||||
|
||||
// Ruft Foo::Foo(const Foo&) auf, aber kopiert lediglich den "Foo" Teil von
|
||||
// Ruft Foo::Foo(const Foo&) auf, aber kopiert lediglich den "Foo" Teil von
|
||||
// "fooSub". Alle zusätzlichen Member werden verworfen. Diese eigenartige Verhalten
|
||||
// wird auch "object slicing" genannt.
|
||||
FooSub fooSub;
|
||||
@ -1077,10 +1074,10 @@ f1 = f2;
|
||||
|
||||
#include<tuple>
|
||||
|
||||
// Konzeptionell sind Tuples alten Datenstrukturen sehr ähnlich, allerdings haben diese keine
|
||||
// benamten Daten-Member, sondern werden durch die Reihenfolge angesprochen.
|
||||
// Konzeptionell sind Tuple´s alten Datenstrukturen sehr ähnlich, allerdings haben diese keine
|
||||
// bezeichneten Daten-Member, sondern werden durch die Reihenfolge angesprochen.
|
||||
|
||||
// Erstellen des Tuples und das Einfügen eines Werts.
|
||||
// Erstellen des Tuples und das Einfügen eines Werts.
|
||||
auto first = make_tuple(10, 'A');
|
||||
const int maxN = 1e9;
|
||||
const int maxL = 15;
|
||||
@ -1122,8 +1119,7 @@ cout << get<5>(concatenated_tuple) << "\n"; // Ausgabe: 'A'
|
||||
// Die meisten Operatoren in C++ entsprechen denen aus anderen Sprachen
|
||||
|
||||
// Logische Operatoren.
|
||||
|
||||
// C++ verwendet so genannte "Short-circuit" Evaluierung für boolean-Ausdrücke.
|
||||
// C++ verwendet so genannte "Short-circuit" Evaluierung für Boolean-Ausdrücke.
|
||||
// Das zweite Argument wird ausgeführt bzw. evaluiert, wenn das erste Argument genügt,
|
||||
// um den Ausdruck zu bestimmen.
|
||||
|
||||
|
362
dhall.html.markdown
Normal file
362
dhall.html.markdown
Normal file
@ -0,0 +1,362 @@
|
||||
---
|
||||
language: Dhall
|
||||
filename: learndhall.dhall
|
||||
contributors:
|
||||
- ["Gabriel Gonzalez", "http://www.haskellforall.com/"]
|
||||
---
|
||||
|
||||
Dhall is a programmable configuration language that provides a non-repetitive
|
||||
alternative to YAML.
|
||||
|
||||
You can think of Dhall as: JSON + functions + types + imports
|
||||
|
||||
Note that while Dhall is programmable, Dhall is not Turing-complete. Many
|
||||
of Dhall's features take advantage of this restriction to provider stronger
|
||||
safety guarantees and more powerful tooling.
|
||||
|
||||
```haskell
|
||||
-- Single-line comment
|
||||
|
||||
{- Multi-line comment
|
||||
|
||||
Unicode is fine 🙂
|
||||
|
||||
This file is a valid Dhall expression that evaluates to a large record
|
||||
collecting the results of each step.
|
||||
|
||||
You can view the results by interpreting the file:
|
||||
|
||||
$ dhall --file learndhall.dhall
|
||||
|
||||
{- Comments can be nested -}
|
||||
-}
|
||||
|
||||
let greeting = "Hello, world!"
|
||||
|
||||
let fruits = "🍋🍓🍍🍉🍌"
|
||||
|
||||
let interpolation = "Enjoy some delicious fruit: ${fruits}"
|
||||
|
||||
let multilineText {- Inline comments work, too -} =
|
||||
''
|
||||
Leading whitespace is stripped from multi-line text literals.
|
||||
|
||||
That means you can freely indent or dedent a text literal without
|
||||
changing the result.
|
||||
|
||||
Relative indentation within the literal is still preserved.
|
||||
|
||||
Other than that, the text literal is preserved verbatim, similar to a
|
||||
"literal" YAML multiline string.
|
||||
''
|
||||
|
||||
let bool = True
|
||||
|
||||
-- Type annotations on bindings are optional, but helpful, so we'll use them
|
||||
let annotation : Bool = True
|
||||
|
||||
let renderedBool : Text = if bool then "True" else "False"
|
||||
|
||||
-- Natural numbers are non-negative and are unsigned
|
||||
let naturalNumber : Natural = 42
|
||||
|
||||
-- Integers may be negative, but require an explicit sign, even if positive
|
||||
let positiveInteger : Integer = +1
|
||||
|
||||
let negativeInteger : Integer = -12
|
||||
|
||||
let pi : Double = 3.14159265359
|
||||
|
||||
{- You can use a wider character range for identifiers (such as quotation
|
||||
marks and whitespace) if you quote them using backticks
|
||||
-}
|
||||
let `Avogadro's Number` : Double = 6.0221409e+23
|
||||
|
||||
let origin : { x : Double, y : Double } = { x = 0.0, y = 0.0 }
|
||||
|
||||
let somePrimes : List Natural = [ 2, 3, 5, 7, 11 ]
|
||||
|
||||
{- A schema is the same thing as a type
|
||||
|
||||
Types begin with an uppercase letter by convention, but this convention is
|
||||
not enforced
|
||||
-}
|
||||
let Profile : Type
|
||||
= { person :
|
||||
{ name : Text
|
||||
, age : Natural
|
||||
}
|
||||
, address :
|
||||
{ country : Text
|
||||
, state : Text
|
||||
, city : Text
|
||||
}
|
||||
}
|
||||
|
||||
let john : Profile =
|
||||
{ person =
|
||||
{ name = "John Doe"
|
||||
, age = 67
|
||||
}
|
||||
, address =
|
||||
{ country = "United States"
|
||||
, state = "Pennsylvania"
|
||||
, city = "Philadelphia"
|
||||
}
|
||||
}
|
||||
|
||||
let philadelphia : Text = john.address.city
|
||||
|
||||
{- Enum alternatives also begin with an uppercase letter by convention. This
|
||||
convention is not enforced
|
||||
-}
|
||||
let DNA : Type = < Adenine | Cytosine | Guanine | Thymine >
|
||||
|
||||
let dnaSequence : List DNA = [ DNA.Thymine, DNA.Guanine, DNA.Guanine ]
|
||||
|
||||
let compactDNASequence : List DNA =
|
||||
let a = DNA.Adenine
|
||||
let c = DNA.Cytosine
|
||||
let g = DNA.Guanine
|
||||
let t = DNA.Thymine
|
||||
in [ c, t, t, a, t, c, g, g, c ]
|
||||
|
||||
-- You can transform enums by providing a record with one field per alternative
|
||||
let theLetterG : Text =
|
||||
merge
|
||||
{ Adenine = "A"
|
||||
, Cytosine = "C"
|
||||
, Guanine = "G"
|
||||
, Thymine = "T"
|
||||
}
|
||||
DNA.Guanine
|
||||
|
||||
let presentOptionalValue : Optional Natural = Some 1
|
||||
|
||||
let absentOptionalValue : Optional Natural = None Natural
|
||||
|
||||
let points : List { x : Double, y : Double } =
|
||||
[ { x = 1.1, y = -4.2 }
|
||||
, { x = 4.4, y = -3.0 }
|
||||
, { x = 8.2, y = -5.5 }
|
||||
]
|
||||
|
||||
{- `Natural -> List Natural` is the type of a function whose input type is a
|
||||
`Natural` and whose output type is a `List Natural`
|
||||
|
||||
All functions in Dhall are anonymous functions (a.k.a. "lambdas"),
|
||||
which you can optionally give a name
|
||||
|
||||
For example, the following function is equivalent to this Python code:
|
||||
|
||||
lambda n : [ n, n + 1 ]
|
||||
|
||||
... and this JavaScript code:
|
||||
|
||||
function (n) { return [ n, n + 1 ]; }
|
||||
-}
|
||||
let exampleFunction : Natural -> List Natural =
|
||||
\(n : Natural) -> [ n, n + 1 ]
|
||||
|
||||
-- Dhall also supports Unicode syntax, but this tutorial will stick to ASCII
|
||||
let unicodeFunction : Natural → List Natural =
|
||||
λ(n : Natural) → [ n, n + 1 ]
|
||||
|
||||
-- You don't need to parenthesize function arguments
|
||||
let exampleFunctionApplication : List Natural =
|
||||
exampleFunction 2
|
||||
|
||||
let functionOfMultipleArguments : Natural -> Natural -> List Natural =
|
||||
\(x : Natural) -> \(y : Natural) -> [ x, y ]
|
||||
|
||||
let functionAppliedToMultipleArguments : List Natural =
|
||||
functionOfMultipleArguments 2 3
|
||||
|
||||
{- Same as `exampleFunction` except we gave the function's input type a
|
||||
name: "n"
|
||||
-}
|
||||
let namedArgumentType : forall (n : Natural) -> List Natural =
|
||||
\(n : Natural) -> [ n, n + 1 ]
|
||||
|
||||
{- If you name a function's input type, you can use that name later within the
|
||||
same type
|
||||
|
||||
This lets you write a function that works for more than one type of input
|
||||
(a.k.a. a "polymorphic" function)
|
||||
-}
|
||||
let duplicate : forall (a : Type) -> a -> List a =
|
||||
\(a : Type) -> \(x : a) -> [ x, x ]
|
||||
|
||||
let duplicatedNumber : List Natural =
|
||||
duplicate Natural 2
|
||||
|
||||
let duplicatedBool : List Bool =
|
||||
duplicate Bool False
|
||||
|
||||
{- The language also has some built-in polymorphic functions, such as:
|
||||
|
||||
List/head : forall (a : Type) -> List a -> Optional a
|
||||
-}
|
||||
let firstPrime : Optional Natural = List/head Natural somePrimes
|
||||
|
||||
let functionOfARecord : { x : Natural, y : Natural } -> List Natural =
|
||||
\(args : { x : Natural, y : Natural }) -> [ args.x, args.y ]
|
||||
|
||||
let functionAppliedToARecord : List Natural =
|
||||
functionOfARecord { x = 2, y = 5 }
|
||||
|
||||
{- All type conversions are explicit
|
||||
|
||||
`Natural/show` is a built-in function of the following type:
|
||||
|
||||
Natural/show : Natural -> Text
|
||||
|
||||
... that converts `Natural` numbers to their `Text` representation
|
||||
-}
|
||||
let typeConversion : Natural -> Text =
|
||||
\(age : Natural) -> "I am ${Natural/show age} years old!"
|
||||
|
||||
-- A template is the same thing as a function whose output type is `Text`
|
||||
let mitLicense : { year : Natural, copyrightHolder : Text } -> Text =
|
||||
\(args : { year : Natural, copyrightHolder : Text }) ->
|
||||
''
|
||||
Copyright ${Natural/show args.year} ${args.copyrightHolder}
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
''
|
||||
|
||||
-- Template instantiation is the same thing as function application
|
||||
let templatedLicense : Text =
|
||||
mitLicense { year = 2019, copyrightHolder = "Jane Smith" }
|
||||
|
||||
{- You can import expressions by URL
|
||||
|
||||
Also, like Bash, you can import code from your local filesystem (not shown)
|
||||
|
||||
Security-conscious users can pin remotely-imported expressions by adding a
|
||||
semantic integrity check. The interpreter rejects any attempt to tamper with
|
||||
an expression pinned in this way. However, behavior-preserving refactors
|
||||
of imported content will not perturb the hash.
|
||||
|
||||
Imported expressions pinned in this way are also locally cached in a
|
||||
content-addressable store (typically underneath `~/.cache/dhall`)
|
||||
-}
|
||||
let Natural/sum : List Natural -> Natural =
|
||||
https://prelude.dhall-lang.org/Natural/sum
|
||||
sha256:33f7f4c3aff62e5ecf4848f964363133452d420dcde045784518fb59fa970037
|
||||
|
||||
let twentyEight : Natural = Natural/sum somePrimes
|
||||
|
||||
-- A package is the same thing as a (possibly nested) record that you can import
|
||||
let Prelude = https://prelude.dhall-lang.org/package.dhall
|
||||
|
||||
let false : Bool = Prelude.Bool.not True
|
||||
|
||||
-- You can import the raw contents of a file by adding `as Text` to an import
|
||||
let sourceCode : Text = https://prelude.dhall-lang.org/Bool/not as Text
|
||||
|
||||
-- You can import environment variables, too:
|
||||
let presentWorkingDirectory = env:PWD as Text
|
||||
|
||||
-- You can provide a fallback expression if an import fails
|
||||
let home : Optional Text = Some env:HOME ? None Text
|
||||
|
||||
-- Fallback expressions can contain alternative imports of their own
|
||||
let possiblyCustomPrelude =
|
||||
env:DHALL_PRELUDE
|
||||
? https://prelude.dhall-lang.org/package.dhall
|
||||
|
||||
{- Tie everything together by auto-generating configurations for 10 build users
|
||||
using the `generate` function:
|
||||
|
||||
Prelude.List.generate
|
||||
: Natural -> forall (a : Type) -> (Natural -> a) -> List a
|
||||
-}
|
||||
let buildUsers =
|
||||
let makeUser = \(user : Text) ->
|
||||
let home = "/home/${user}"
|
||||
let privateKey = "${home}/.ssh/id_ed25519"
|
||||
let publicKey = "${privateKey}.pub"
|
||||
in { home = home
|
||||
, privateKey = privateKey
|
||||
, publicKey = publicKey
|
||||
}
|
||||
|
||||
let buildUser =
|
||||
\(index : Natural) -> makeUser "build${Natural/show index}"
|
||||
|
||||
let Config =
|
||||
{ home : Text
|
||||
, privateKey : Text
|
||||
, publicKey : Text
|
||||
}
|
||||
|
||||
in Prelude.List.generate 10 Config buildUser
|
||||
|
||||
-- Present all of the results in a final record
|
||||
in { greeting = greeting
|
||||
, fruits = fruits
|
||||
, interpolation = interpolation
|
||||
, multilineText = multilineText
|
||||
, bool = bool
|
||||
, annotation = annotation
|
||||
, renderedBool = renderedBool
|
||||
, naturalNumber = naturalNumber
|
||||
, positiveInteger = positiveInteger
|
||||
, negativeInteger = negativeInteger
|
||||
, pi = pi
|
||||
, `Avogadro's Number` = `Avogadro's Number`
|
||||
, origin = origin
|
||||
, somePrimes = somePrimes
|
||||
, john = john
|
||||
, philadelphia = philadelphia
|
||||
, dnaSequence = dnaSequence
|
||||
, compactDNASequence = compactDNASequence
|
||||
, theLetterG = theLetterG
|
||||
, presentOptionalValue = presentOptionalValue
|
||||
, absentOptionalValue = absentOptionalValue
|
||||
, points = points
|
||||
, exampleFunction = exampleFunction
|
||||
, unicodeFunction = unicodeFunction
|
||||
, exampleFunctionApplication = exampleFunctionApplication
|
||||
, functionOfMultipleArguments = functionOfMultipleArguments
|
||||
, functionAppliedToMultipleArguments = functionAppliedToMultipleArguments
|
||||
, namedArgumentType = namedArgumentType
|
||||
, duplicate = duplicate
|
||||
, duplicatedNumber = duplicatedNumber
|
||||
, duplicatedBool = duplicatedBool
|
||||
, firstPrime = firstPrime
|
||||
, functionOfARecord = functionOfARecord
|
||||
, functionAppliedToARecord = functionAppliedToARecord
|
||||
, typeConversion = typeConversion
|
||||
, mitLicense = mitLicense
|
||||
, templatedLicense = templatedLicense
|
||||
, twentyEight = twentyEight
|
||||
, false = false
|
||||
, sourceCode = sourceCode
|
||||
, presentWorkingDirectory = presentWorkingDirectory
|
||||
, home = home
|
||||
, buildUsers = buildUsers
|
||||
}
|
||||
```
|
||||
|
||||
To learn more, visit the official website, which also lets you try the
|
||||
language live in your browser:
|
||||
|
||||
* [https://dhall-lang.org](http://dhall-lang.org/)
|
320
emacs.html.markdown
Normal file
320
emacs.html.markdown
Normal file
@ -0,0 +1,320 @@
|
||||
---
|
||||
category: tool
|
||||
tool: emacs
|
||||
filename: emacs.txt
|
||||
contributors:
|
||||
- ["Joseph Riad", "https://github.com/Joseph-Riad"]
|
||||
---
|
||||
|
||||
Emacs started its life as ["the extensible, customizable display
|
||||
editor"](https://www.gnu.org/software/emacs/emacs-paper.html) and grew
|
||||
over the years into a full-blown ecosystem. Many tasks, usually
|
||||
relegated to a diverse set of tools can be accomplished from within
|
||||
Emacs in a consistent, familiar interface. Examples include directory
|
||||
management, viewing PDF documents, editing files over SSH, managing git
|
||||
repos,… (the list is quite long). In short, Emacs is yours to make of it
|
||||
what you will: the spectrum of users varies from those who use it to
|
||||
edit text files to extreme purists who use it to virtually replace their
|
||||
operating system.
|
||||
|
||||
Emacs is extensible via a specialized dialect of Lisp known as Emacs
|
||||
Lisp (Elisp) which has a lot of macros geared towards editing text and
|
||||
managing text buffers. Any key (combination) you use in Emacs is bound
|
||||
to an Emacs Lisp function and may be remapped to any other function,
|
||||
including ones you write
|
||||
yourself.
|
||||
|
||||
# Key Notation
|
||||
|
||||
``` text
|
||||
The Emacs manual and the community in general uses a convention to refer to different key combinations used within Emacs. Specifically, Emacs has the notion of a "modifier key" that is pressed along with another key to modify its action.
|
||||
|
||||
An example of this notation is "C-c". In this key combination "C" is the modifier and stands for the "Ctrl" key and "c" is the key whose action is being modified (the literal character "c").
|
||||
|
||||
The modifier shorthand:
|
||||
"C-" --> The "CTRL" key
|
||||
"M-" --> The "Meta" key (usually, the "Alt" key)
|
||||
"s-" --> The "Super" key (the "Cmd" key on Macs and the "Windows" key on PCs)
|
||||
|
||||
There are other, less commonly used modifiers that I will not get into here.
|
||||
|
||||
The key combination "C-x C-s" means you press "Ctrl+x" followed by "Ctrl+s"
|
||||
|
||||
In addition to the above modifiers, the special keys "Esc", "Return (Enter)" and "Shift" are denoted by "ESC", "RET" and "S", respectively.
|
||||
```
|
||||
|
||||
# Basic Emacs Concepts
|
||||
|
||||
Here, I discuss some basic Emacs concepts and terminology that may be
|
||||
confusing to newcomers (especially to people used to Vim terminology)
|
||||
|
||||
- A bunch of text that Emacs is editing is known as a **buffer**
|
||||
- A buffer does not necessarily correspond to an actual file on disk.
|
||||
It may be just a bunch of text in memory.
|
||||
- When a buffer corresponds to a file on disk, we say that the buffer
|
||||
is **visiting** that file.
|
||||
- Emacs typically has many buffers open at once.
|
||||
- The display of Emacs may be split into different **windows** (not to
|
||||
be confused with your operating system's windows: the operating
|
||||
system window for Emacs can have multiple Emacs windows inside it).
|
||||
- An operating system window for Emacs is called an Emacs **frame**.
|
||||
Thus, when the Emacs manual talks about opening a new frame, this
|
||||
essentially means opening a new OS *window* containing an(other)
|
||||
instance of Emacs.
|
||||
- The concepts conventionally known as cutting and pasting are
|
||||
referred to as **killing** and **yanking**, respectively in Emacs
|
||||
parlance.
|
||||
- The current position of the cursor is called the **point** in Emacs.
|
||||
Technically, **point** is defined as the position right before the
|
||||
character where the cursor currently is.
|
||||
- Finally, each buffer may have several **modes** associated with it:
|
||||
a **major mode** and possibly several **minor modes**.
|
||||
- The **major mode** defines the main behavior of Emacs in the
|
||||
currently selected buffer. This can be roughly thought of as the
|
||||
file type. For example, if you're editing a Python file, the major
|
||||
mode is (by default) `python-mode` which causes Emacs to highlight
|
||||
Python syntax and automatically indent and outdent your code blocks
|
||||
as syntactically required by your Python code.
|
||||
- **Minor modes** define subtle changes in behavior and several minor
|
||||
modes may be active at once in the same buffer. An example minor
|
||||
mode is `flyspell-mode` which automatically highlights spelling
|
||||
errors in your
|
||||
buffer.
|
||||
|
||||
# Navigation Basics
|
||||
|
||||
``` text
|
||||
The GUI version of Emacs can be navigated with the mouse like you would expect from a conventional GUI text editor.
|
||||
|
||||
The aim here is to focus on navigation solely using the keyboard as this enhances productivity immensely.
|
||||
|
||||
|
||||
* Line movement
|
||||
|
||||
C-n --> Next line
|
||||
C-p --> Previous line
|
||||
|
||||
* Character movement
|
||||
|
||||
C-f --> Go forward one character
|
||||
C-b --> Go backward one character
|
||||
|
||||
* Word movement
|
||||
|
||||
M-f --> Go forward one word
|
||||
M-b --> Go backward one word
|
||||
|
||||
* Sentence movement
|
||||
|
||||
M-a --> Move to the beginning of the sentence
|
||||
M-e --> Move to the end of the sentence
|
||||
|
||||
* Beginning and end of line
|
||||
|
||||
C-a --> Move to the beginning of the line
|
||||
C-e --> Move to the end of the line
|
||||
|
||||
* Beginning and end of buffer
|
||||
|
||||
M-< ("Meta+Shift+,") --> Go to the beginning of the buffer
|
||||
M-> ("Meta+Shift+.") --> Go to the end of the buffer
|
||||
|
||||
* Screen movement
|
||||
|
||||
C-v --> Scroll down by one screen-full (the last two lines of the previous screen are kept as overlap for a smoother transition)
|
||||
M-v --> Scroll up by one screen-full (same as above but with the first two lines)
|
||||
|
||||
* Centering the screen
|
||||
|
||||
C-l --> Move current line to the screen's center
|
||||
|
||||
The above key combination actually cycles through different states depending on how many times it's been pressed.
|
||||
|
||||
C-l --> Move current line to the screen's center
|
||||
C-l C-l --> Move current line to the top of the screen
|
||||
C-l C-l C-l --> Restore the position of the current line to where it was before the first C-l was pressed
|
||||
|
||||
If you press "C-l" a 4th time, it cycles back to centering the current line.
|
||||
|
||||
* Repeating movement commands
|
||||
|
||||
Most movement commands take a numerical prefix argument that says "repeat the following command that many times".
|
||||
|
||||
Example:
|
||||
|
||||
C-u 3 C-p --> Go up 3 lines
|
||||
C-u 5 C-f --> Go forward 5 characters
|
||||
|
||||
One notable exception are the screen scrolling commands:
|
||||
|
||||
C-u 3 C-v --> Scroll downward 3 lines (maintaining the position of the cursor)
|
||||
```
|
||||
|
||||
Bonus: many of the above navigation commands are the default navigation
|
||||
commands in Bash (e.g. pressing "C-b" while entering a Bash command
|
||||
takes you back one
|
||||
character).
|
||||
|
||||
# File editing basics
|
||||
|
||||
``` text
|
||||
* Quitting Emacs [ Now you can't say you don't know how to quit Emacs :-) ]
|
||||
|
||||
C-x C-c --> Quit Emacs and get prompted to save any unsaved files (buffers not visiting a file will simply be discarded unless you're running in client-server mode)
|
||||
|
||||
* Saving a buffer
|
||||
|
||||
C-x C-s --> Save the current buffer. If not visiting a file, it will prompt you for a file name to use to save the buffer.
|
||||
|
||||
* Searching within a buffer
|
||||
|
||||
C-s --> Search forwards within the buffer. Search is incremental and case-insensitive by default.
|
||||
Press C-s to move to the next match.
|
||||
If you press "RET", point is moved to the currently highlighted word and the search ends.
|
||||
C-r --> Same as C-s except it searches backward
|
||||
|
||||
C-_ or C-/ --> Undo the last action. Keep pressing it to move up the undo tree.
|
||||
C-? or M-_ --> Redo the previous change
|
||||
|
||||
The "undo" and "redo" commands can take prefix numerical arguments to undo or redo that many actions:
|
||||
|
||||
C-u 3 C-_ --> Undo the last 3 changes.
|
||||
```
|
||||
|
||||
# Executing Elisp Functions
|
||||
|
||||
``` text
|
||||
You can execute any currently loaded Elisp functions (including ones you have written yourself) via "M-x"
|
||||
|
||||
M-x RET --> Prompts you for name of function to execute (Tab completion is available).
|
||||
|
||||
Example:
|
||||
|
||||
M-x RET search-forward-regexp RET --> Prompts you for a regular expression and searches forward in the buffer for it
|
||||
```
|
||||
|
||||
# Emacs Configuration
|
||||
|
||||
Emacs is configured using Elisp. On startup, it looks for a
|
||||
configuration file either in `~/.emacs` or `~/.emacs.d/init.el` where
|
||||
`~` refers to your home directory. If you're on Windows, consult [this
|
||||
article](https://www.gnu.org/software/emacs/manual/html_node/efaq-w32/Location-of-init-file.html)
|
||||
for the appropriate location of your configuration file.
|
||||
|
||||
# Vim inside Emacs
|
||||
|
||||
If you are considering the transition from Vim to Emacs and you're put
|
||||
off by the non-modal nature of Emacs editing, there is an Emacs
|
||||
extension known as `evil-mode` which lets you have many Vim concepts
|
||||
inside Emacs. Here are some things added to Emacs by `evil-mode`:
|
||||
|
||||
- Modal editing: you get normal, insert, visual and block visual modes
|
||||
like Vim. In addition, you get an "Emacs" mode where movement and
|
||||
navigation follow the Emacs bindings.
|
||||
- Same movement keys as Vim in normal mode
|
||||
- Leader key combinations
|
||||
- Pressing ":" in normal mode allows you to execute commands
|
||||
(including system commands)
|
||||
|
||||
In my own experience, `evil-mode` helps make the transition seamless and
|
||||
allows you to blend the arguably more intuitive and ergonomic
|
||||
keybindings of Vim with the unbridled power of Emacs for a truly
|
||||
superior editing experience.
|
||||
|
||||
# Discoverable Help
|
||||
|
||||
Emacs features a pretty powerful help system that allows you to discover
|
||||
new functionality all the
|
||||
time.
|
||||
|
||||
``` text
|
||||
Obtaining help on specific topics. Tab completion is available for function and variable names.
|
||||
|
||||
C-h f RET --> Prompts you for the name of an elisp function and
|
||||
displays help text on it along with a clickable link
|
||||
to its source code.
|
||||
C-h v RET --> Same as above with variables
|
||||
|
||||
C-h k RET --> Allows you to enter a key combination and displays the
|
||||
name of the elisp function bound to it.
|
||||
|
||||
Searching for help:
|
||||
|
||||
C-h a --> Prompts you for a string to search for a command in the
|
||||
help system. Similar to the 'apropos' or 'man -k'
|
||||
commands in Unix systems.
|
||||
|
||||
Starting a tutorial:
|
||||
|
||||
C-h C-t --> Starts a tutorial designed to familiarize you with
|
||||
basic Emacs functionality.
|
||||
```
|
||||
|
||||
# Emacs "Killer Apps"
|
||||
|
||||
As I hinted above, Emacs functionality goes way beyond being a mere text
|
||||
editor. I will list here a couple of Emacs "apps" that are fairly
|
||||
powerful and popular and may interest you in and of themselves.
|
||||
|
||||
## Org
|
||||
|
||||
Technnically, `org-mode`, a major mode for buffer editing that provides
|
||||
organizational tools. It is very difficult to succinctly describe what
|
||||
Org can do because it's a behemoth of a tool that has many diverse uses
|
||||
to different people. I will attempt to describe the main features I use
|
||||
briefly.
|
||||
|
||||
- Divide your file into sections and sub-sections for easy outlining
|
||||
and organizing of concepts.
|
||||
- Different headings in the outline are foldable/expandable so that
|
||||
you can focus on what you need to focus on and eliminate
|
||||
distractions.
|
||||
- You can maintain a TODO list within Org
|
||||
- You can compile TODO lists from many files into an agenda
|
||||
- Track the time you spend on each TODO task
|
||||
- Manage tables in plain text (including spreadsheet-like
|
||||
capabilities)
|
||||
- Using the extension `org-babel`, write and execute code blocks in
|
||||
your file. The results are captured and are re-usable within the
|
||||
file itself. Think Jupyter notebook for any language.
|
||||
- Display inline images and LaTeX formulas as images within your file
|
||||
(makes for a great note-taking system and/or personal wiki)
|
||||
- Export your file into many different formats (LaTeX, PDF, html,…)
|
||||
|
||||
Org mode is a very powerful tool to add to your productivity arsenal
|
||||
and, on a personal note, was the reason that caused me to start using
|
||||
Emacs after years of using Vim.
|
||||
|
||||
## Magit
|
||||
|
||||
This is a frontend to `git` from within Emacs. It features a very
|
||||
intuitive and discoverable interface, yet exposes very powerful
|
||||
functionality that allows you to manage commits at the chunk level,
|
||||
inspect diffs, rebase, cherry-pick, … all from within the comfort of
|
||||
your own editor.
|
||||
|
||||
# A Word of Advice
|
||||
|
||||
If you are considering using Emacs, a common trap that beginning users
|
||||
fall into is to copy someone else's configuration file and use it as is.
|
||||
I highly recommend against doing this for several reasons:
|
||||
|
||||
- It will discourage you from learning and finding things out for
|
||||
yourself
|
||||
- Someone else's configuration will probably contain many things
|
||||
relevant to them that you won't need or ever use.
|
||||
- It defeats the purpose of having a customizable text editor that can
|
||||
fit your own needs.
|
||||
|
||||
What I encourage you to do is to look at other people's configurations
|
||||
and seek to understand them and adapt only what makes sense to you. You
|
||||
can find out about new features of Emacs through many YouTube videos,
|
||||
screencasts or blog posts and then learn for yourself how to add them to
|
||||
your configuration and workflow. This way, you grow your configuration
|
||||
incrementally while increasing your knowledge of Emacs along the way.
|
||||
|
||||
# Additional Resources
|
||||
|
||||
- [The GNU Emacs Manual](https://www.gnu.org/software/emacs/manual/emacs.html)
|
||||
- [Emacs Stack Exchange](https://emacs.stackexchange.com/)
|
||||
- [Emacs Wiki](https://www.emacswiki.org/emacs/EmacsWiki)
|
617
it-it/javascript-it.html.markdown
Normal file
617
it-it/javascript-it.html.markdown
Normal file
@ -0,0 +1,617 @@
|
||||
---
|
||||
language: javascript
|
||||
contributors:
|
||||
- ["Adam Brenecki", "http://adam.brenecki.id.au"]
|
||||
- ["Ariel Krakowski", "http://www.learneroo.com"]
|
||||
translators:
|
||||
- ["vinniec", "https://github.com/vinniec"]
|
||||
filename: javascript-it.js
|
||||
lang: it-it
|
||||
---
|
||||
|
||||
JavaScript è stato creato da Netscape di Brendan Eich nel 1995. È stato originariamente pensato come un semplice linguaggio di scripting per i siti web, complementare all'uso di java per applicazioni più complesse ma la sua stretta integrazione con le pagine Web e il supporto integrato con esse ha causato il suo divenire più comune di Java per i frontend web.
|
||||
|
||||
Tuttavia JavaScript non è semplicemente limitato ai web browser: Node.js è un progetto che fornisce una runtime standalone dell'engine JavaScript V8 per Google Chrome, sta diventando sempre più popolare.
|
||||
|
||||
JavaScript ha una sintassi C-like, quindi se usate linguaggi come C o Java, molta della sintassi di base sarà già familiare. A dispetto di questo, e a dispetto del nome similare, il modello a oggetti di JavaScript è significativamente diverso da quello di Java.
|
||||
|
||||
```js
|
||||
// I commenti a singola linea iniziano con due slash.
|
||||
/* I commenti multilinea cominciano con uno slash e un asterisco,
|
||||
e terminano con un asterisco e uno slash */
|
||||
|
||||
// Le istruzioni possono essere terminate con ;
|
||||
doStuff();
|
||||
|
||||
// ... ma non devono esserci per forza, i punti e virgola vengono automaticamente inseriti
|
||||
// dove c'è un newline, ad eccezione di alcuni casi.
|
||||
doStuff()
|
||||
|
||||
// Poiché questi casi possono causare risultati inaspettati, noi continueremo ad usare
|
||||
// i punti e virgola in questa guida.
|
||||
|
||||
///////////////////////////////////
|
||||
// 1. Numeri, Stringe e Operatori
|
||||
|
||||
// JavaScript ha un tipo numero (che è a 64-bit IEEE 754 double).
|
||||
// Double ha una mantissa di 52-bit che è abbastanza per memorizzare interi
|
||||
// fino a 9x10¹⁵ per essere precisi.
|
||||
3; // = 3
|
||||
1.5; // = 1.5
|
||||
|
||||
// Alcuni lavori aritmetici di base come ci si può aspettare.
|
||||
1 + 1; // = 2
|
||||
0.1 + 0.2; // = 0.30000000000000004
|
||||
8 - 1; // = 7
|
||||
10 * 2; // = 20
|
||||
35 / 5; // = 7
|
||||
|
||||
// inclusa la divisione con la virgola.
|
||||
5 / 2; // = 2.5
|
||||
|
||||
// E il modulo.
|
||||
10 % 2; // = 0
|
||||
30 % 4; // = 2
|
||||
18.5 % 7; // = 4.5
|
||||
|
||||
// Anche le operazioni binarie funzionano; quando effettuate una operazione binaria il vostro numero decimale
|
||||
// è convertito in un intero con segno *fino a* 32 bit..
|
||||
1 << 2; // = 4
|
||||
|
||||
// Le precedenza è subordinata dalle parentesi.
|
||||
(1 + 3) * 2; // = 8
|
||||
|
||||
// Ci sono tre valori speciali che non sono numeri reali:
|
||||
Infinity; // ad esempio il risultato di 1/0
|
||||
-Infinity; // ad esempio il risultato di -1/0
|
||||
NaN; // ad esempio il risultato di 0/0, sta per 'Not a Number'
|
||||
|
||||
// Ci sono anche i tipi booleani.
|
||||
true;
|
||||
false;
|
||||
|
||||
// Le stringe sono create con ' oppure ".
|
||||
'abc';
|
||||
"Hello, world";
|
||||
|
||||
// La negazione usa il ! simbolo
|
||||
!true; // = false
|
||||
!false; // = true
|
||||
|
||||
// L'uguaglianza è ===
|
||||
1 === 1; // = true
|
||||
2 === 1; // = false
|
||||
|
||||
// L'inuguaglianza è !==
|
||||
1 !== 1; // = false
|
||||
2 !== 1; // = true
|
||||
|
||||
// Altre comparazioni
|
||||
1 < 10; // = true
|
||||
1 > 10; // = false
|
||||
2 <= 2; // = true
|
||||
2 >= 2; // = true
|
||||
|
||||
// Le stringhe si concatenano con il +
|
||||
"Hello " + "world!"; // = "Hello world!"
|
||||
|
||||
// ... che funziona con qualcosa in più delle semplici stringhe
|
||||
"1, 2, " + 3; // = "1, 2, 3"
|
||||
"Hello " + ["world", "!"]; // = "Hello world,!"
|
||||
|
||||
// e sono comparate con < e >
|
||||
"a" < "b"; // = true
|
||||
|
||||
// La comparazione con conversione implicita si fa con il doppio uguale...
|
||||
"5" == 5; // = true
|
||||
null == undefined; // = true
|
||||
|
||||
// ...ammenoché non si usi ===
|
||||
"5" === 5; // = false
|
||||
null === undefined; // = false
|
||||
|
||||
// ...che può provocare strani comportamenti...
|
||||
13 + !0; // 14
|
||||
"13" + !0; // '13true'
|
||||
|
||||
// Si può accedere ai caratteri di una stringa con `charAt`
|
||||
"This is a string".charAt(0); // = 'T'
|
||||
|
||||
// ...o usando le `substring` per ottenere una parte.
|
||||
"Hello world".substring(0, 5); // = "Hello"
|
||||
|
||||
// `length` è una proprietà, quindi non usate le ().
|
||||
"Hello".length; // = 5
|
||||
|
||||
// Ci sono anche `null` e `undefined`.
|
||||
null; // usato per indicato deliberatamente un non-valore
|
||||
undefined; // usato per indicare un valore che attualmente non è presente (sebbene
|
||||
// `undefined` sia un valore a sua stessa volta)
|
||||
|
||||
// false, null, undefined, NaN, 0 e "" sono falsi; tutto il resto è vero.
|
||||
// Notare che 0 è falso e "0" è vero, nonostante 0 == "0".
|
||||
|
||||
///////////////////////////////////
|
||||
// 2. Variabili, Array e Oggetti
|
||||
|
||||
// Le variabili sono dichiarate con la parola chiave `var`. JavaScript è tipato
|
||||
// dinamicamente, quindi non serve specificare il tipo. L'assegnamento usa un carattere `=`
|
||||
// singolo.
|
||||
var someVar = 5;
|
||||
|
||||
// Se si toglie la parola chiave var non si otterrà un errore...
|
||||
someOtherVar = 10;
|
||||
|
||||
// ...ma la tua variabile sarà creata con visibilità globale e non
|
||||
// nel blocco dove la si è definita.
|
||||
|
||||
// Le variabili dichiarate senza essere definite vengono impostate come undefined.
|
||||
var someThirdVar; // = undefined
|
||||
|
||||
// Se si vuole dichiarare una coppia di variabili, lo si può fare usando una virgola
|
||||
// come separatore
|
||||
var someFourthVar = 2, someFifthVar = 4;
|
||||
|
||||
// C'è una scorciatoia per effettuare operazioni matematiche sulle variabili:
|
||||
someVar += 5; // equivalente di someVar = someVar + 5; someVar vale 10 ora
|
||||
someVar *= 10; // ora someVar è 100
|
||||
|
||||
// e un ulteriore scorciatoia per aggiungere o sottrarre 1
|
||||
someVar++; // ora someVar è 101
|
||||
someVar--; // di nuovo 100
|
||||
|
||||
// Gli array sono liste ordinati di valori, di qualsiasi tipo.
|
||||
var myArray = ["Hello", 45, true];
|
||||
|
||||
// Si può accedere ai loro membri usando la sintassi sottoscritta con le parentesi quadra.
|
||||
// Gli indici degli array iniziano a zero.
|
||||
myArray[1]; // = 45
|
||||
|
||||
// Gli Array sono mutabili e di dimensione variabile.
|
||||
myArray.push("World");
|
||||
myArray.length; // = 4
|
||||
|
||||
// Aggiungere/Modificare in un indice preciso
|
||||
myArray[3] = "Hello";
|
||||
|
||||
// Aggiungere e rimovere un elemento dall'inizio o dalla fine di un array
|
||||
myArray.unshift(3); // Aggiungere come primo elemento
|
||||
someVar = myArray.shift(); // Rimuovere il primo elemento e restituirlo
|
||||
myArray.push(3); // Aggiungere come ultimo elemento
|
||||
someVar = myArray.pop(); // Rimuovere l'ultimo elemento e restituirlo
|
||||
|
||||
// Unire tutti gli elementi di un array con un punto e virgola
|
||||
var myArray0 = [32,false,"js",12,56,90];
|
||||
myArray0.join(";") // = "32;false;js;12;56;90"
|
||||
|
||||
// Ottenere un subarray di elementi dall'indice 1 (incluso) al 4 (escluso)
|
||||
myArray0.slice(1,4); // = [false,"js",12]
|
||||
|
||||
// Rimuovere 4 elementi partendo dall'indice 2 e inserirci delle stringhe
|
||||
// "hi","wr" e "ld"; restituiscono i subarray rimossi
|
||||
myArray0.splice(2,4,"hi","wr","ld"); // = ["js",12,56,90]
|
||||
// myArray0 === [32,false,"hi","wr","ld"]
|
||||
|
||||
// Gli oggetti di JavaScript sono equivalenti ai "dizionari" o "mappe" in altri
|
||||
// linguaggi: una collezione non ordinata di coppie di chiave-valore.
|
||||
var myObj = {key1: "Hello", key2: "World"};
|
||||
|
||||
// Le chiavi sono stringhe, ma non è necessario quotarle se sono identificatori
|
||||
// JavaScript validi. I valori possono essere di ogni tipo.
|
||||
var myObj = {myKey: "myValue", "my other key": 4};
|
||||
|
||||
// Gli attributi degli oggetti possono essere acceduti usando la sintassi "subscript",
|
||||
myObj["my other key"]; // = 4
|
||||
|
||||
// ... o usando la notazione puntata fornendo una chiave che sia un identificatore valido.
|
||||
myObj.myKey; // = "myValue"
|
||||
|
||||
// Gli oggetti sono mutabilil; i valori possono essere cambiati e nuove chiavi possono essere aggiunte.
|
||||
myObj.myThirdKey = true;
|
||||
|
||||
// se si prova ad accedere ad un valore che non è stato ancora impostato, si otterrà undefined.
|
||||
myObj.myFourthKey; // = undefined
|
||||
|
||||
///////////////////////////////////
|
||||
// 3. Strutture logiche e di controllo.
|
||||
|
||||
// La struttura `if` funziona come ci si aspetta.
|
||||
var count = 1;
|
||||
if (count == 3){
|
||||
// eseguito se count vale 3
|
||||
} else if (count == 4){
|
||||
// eseguito se count vale 4
|
||||
} else {
|
||||
// eseguito se count non è né 3 e né 4
|
||||
}
|
||||
|
||||
// Così come il `while`.
|
||||
while (true){
|
||||
// Un ciclo infinito!
|
||||
}
|
||||
|
||||
// I cicli do-while sono come i cicli while ad eccezione che loro iterano almeno una volta.
|
||||
var input;
|
||||
do {
|
||||
input = getInput();
|
||||
} while (!isValid(input));
|
||||
|
||||
// Il ciclo `for` è lo stesso di C e di Java:
|
||||
// inizializzazione, condizione di proseguimento; iterazione.
|
||||
for (var i = 0; i < 5; i++){
|
||||
// verrà eseguito 5 volte
|
||||
}
|
||||
|
||||
// Uscire forzatamente da un un ciclo etichettato è simile a java:
|
||||
outer:
|
||||
for (var i = 0; i < 10; i++) {
|
||||
for (var j = 0; j < 10; j++) {
|
||||
if (i == 5 && j ==5) {
|
||||
break outer;
|
||||
// esce fuori dal ciclo outer invece che solo da quello più interno
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// L'istruzione for/in permette l'iterazione sulle proprietà di un oggetto.
|
||||
var description = "";
|
||||
var person = {fname:"Paul", lname:"Ken", age:18};
|
||||
for (var x in person){
|
||||
description += person[x] + " ";
|
||||
} // description = 'Paul Ken 18 '
|
||||
|
||||
// L'istruzione for/of permette l'iterazione su oggetti iterabili (inclusi i built-in String,
|
||||
// Array, es. gli argomenti Array-like o gli oggetti NodeList, TypedArray, Map e Set,
|
||||
// e gli iterabili decisi dall'utente).
|
||||
var myPets = "";
|
||||
var pets = ["cat", "dog", "hamster", "hedgehog"];
|
||||
for (var pet of pets){
|
||||
myPets += pet + " ";
|
||||
} // myPets = 'cat dog hamster hedgehog '
|
||||
|
||||
// && è la congiunzione logica, || è la disgiunione logica
|
||||
if (house.size == "big" && house.colour == "blue"){
|
||||
house.contains = "bear";
|
||||
}
|
||||
if (colour == "red" || colour == "blue"){
|
||||
// i colori sono sia rossi che blu
|
||||
}
|
||||
|
||||
// && e || "short circuit", utili per impostare i valori di default.
|
||||
var name = otherName || "default";
|
||||
|
||||
// L'istruzione `switch` controlla l'uguaglianza con `===`.
|
||||
// Usare 'break' dopo ogni caso
|
||||
// oppure i casi dopo quello corretto verranno eseguiti comunque.
|
||||
grade = 'B';
|
||||
switch (grade) {
|
||||
case 'A':
|
||||
console.log("Great job");
|
||||
break;
|
||||
case 'B':
|
||||
console.log("OK job");
|
||||
break;
|
||||
case 'C':
|
||||
console.log("You can do better");
|
||||
break;
|
||||
default:
|
||||
console.log("Oy vey");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// 4. Funzioni, Visibilità e Closure
|
||||
|
||||
// Le funzioni di JavaScript sono dichiarate con la parolachiave `function`.
|
||||
function myFunction(thing){
|
||||
return thing.toUpperCase();
|
||||
}
|
||||
myFunction("foo"); // = "FOO"
|
||||
|
||||
// Nota che il valore da restituire deve iniziare nella stessa riga della
|
||||
// keyword `return`, altrimenti verrà sempre restituito `undefined` per via due to
|
||||
// dell'inserimento automatico dei punti e virgola. Fare attenzione a questo quando si usa lo stile Allman.
|
||||
function myFunction(){
|
||||
return // <- punto e virgola automaticamente inserito qui
|
||||
{thisIsAn: 'object literal'};
|
||||
}
|
||||
myFunction(); // = undefined
|
||||
|
||||
// Le funzioni di JavaScript sono oggetti di prima classe, quindi possono essere riassegnate
|
||||
// a diversi nomi di variabili e passate ad altre funzioni come argomenti - per esempio,
|
||||
// mentre si fornisce un gestore di eventi:
|
||||
function myFunction(){
|
||||
// questo codice sarà chiamato in 5 secondi
|
||||
}
|
||||
setTimeout(myFunction, 5000);
|
||||
// Nota: setTimeout non è parte del linguaggio JS, ma è fornito dai browser
|
||||
// e da Node.js.
|
||||
|
||||
// Un altra funzione fornita dai browser è setInterval
|
||||
function myFunction(){
|
||||
// questo codice verrà chiamato ogni 5 secondi
|
||||
}
|
||||
setInterval(myFunction, 5000);
|
||||
|
||||
// Gli oggetti funzione non devono essere dichiarati con un nome - potete scrivere
|
||||
// la definizione di una funzione anonima direttamente come argomento di un'altra.
|
||||
setTimeout(function(){
|
||||
// questo codice sarà chiamato in 5 secondi
|
||||
}, 5000);
|
||||
|
||||
// In JavaScript le funzioni hanno una propria visibilità; le funzioni hanno
|
||||
// il loro scope ma gli altri blocchi no.
|
||||
if (true){
|
||||
var i = 5;
|
||||
}
|
||||
i; // = 5 - non è undefined come ci si potrebbe aspettare in un linguaggio con una propria visibilità per blocco
|
||||
|
||||
// Questo ha portato ad un pattern comune di "esecuzione immediata di funzioni
|
||||
// anonime", che previene alle variabili temporanee di finire nella
|
||||
// visibilità globale.
|
||||
(function(){
|
||||
var temporary = 5;
|
||||
// Noi possiamo accedere alla visibilità globale assegnando all' "oggetto globale", che
|
||||
// in un browser web è sempre `windows`. L'oggetto globale potrebbe avere
|
||||
// nomi differenti in ambienti diverso dal browser come Node.js.
|
||||
window.permanent = 10;
|
||||
})();
|
||||
temporary; // solleva ReferenceError
|
||||
permanent; // = 10
|
||||
|
||||
// Una delle più potenti caratteristiche di javascript sono le closure. Se una funzione è
|
||||
// definita dentro un'altra funzione, la funzione interna ha accesso a le variabili
|
||||
// della funzione esterna, anche dopo essere uscita dalla funzione esterna.
|
||||
function sayHelloInFiveSeconds(name){
|
||||
var prompt = "Hello, " + name + "!";
|
||||
// Le funzioni interne sono messe nella visibilità locale in modo predefinito, anche se vengono
|
||||
// dichiarate con `var`.
|
||||
function inner(){
|
||||
alert(prompt);
|
||||
}
|
||||
setTimeout(inner, 5000);
|
||||
// setTimeout è asincrono, quindi la funzione sayHelloInFiveSeconds
|
||||
// esce immediatamente e setTimeout chiamera inner successivamente. Tuttavia,
|
||||
// poiché inner è "chiuso prima" di sayHelloInFiveSeconds, inner ha ancora
|
||||
// accesso alla variabile `prompt` quando viene finalmente richiamato.
|
||||
}
|
||||
sayHelloInFiveSeconds("Adam"); // aprirà un popup con "Hello, Adam!" in 5s
|
||||
|
||||
///////////////////////////////////
|
||||
// 5. Di più sugli oggetti, costruttori e prototipi.
|
||||
|
||||
// Gli oggetti possono contenere funzioni.
|
||||
var myObj = {
|
||||
myFunc: function(){
|
||||
return "Hello world!";
|
||||
}
|
||||
};
|
||||
myObj.myFunc(); // = "Hello world!"
|
||||
|
||||
// Quando una funzione contenuta in un oggetto viene chiamata, essa può accedere a questo oggetto
|
||||
// possono farlo usando la parola chiave `this`.
|
||||
myObj = {
|
||||
myString: "Hello world!",
|
||||
myFunc: function(){
|
||||
return this.myString;
|
||||
}
|
||||
};
|
||||
myObj.myFunc(); // = "Hello world!"
|
||||
|
||||
// Questo ha a che fare con come la funzione è chiamata, non con dove
|
||||
// è definita. Quindi, la nostra funzione non funziona se non è chiamata
|
||||
// nel contesto dell'oggetto.
|
||||
var myFunc = myObj.myFunc;
|
||||
myFunc(); // = undefined
|
||||
|
||||
// Al contrario, una funzione può essere assegnata ad un oggetto e poi accedere ad esso
|
||||
// attraverso `this`, anche se non è stata inserita durante la definizione.
|
||||
var myOtherFunc = function(){
|
||||
return this.myString.toUpperCase();
|
||||
};
|
||||
myObj.myOtherFunc = myOtherFunc;
|
||||
myObj.myOtherFunc(); // = "HELLO WORLD!"
|
||||
|
||||
// Possiamo anche specificare un contesto per una funzione da eseguire quando la invochiamo
|
||||
// usando `call` o `apply`.
|
||||
|
||||
|
||||
var anotherFunc = function(s){
|
||||
return this.myString + s;
|
||||
};
|
||||
anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!"
|
||||
|
||||
// La funzione `apply` è quasi identica, ma prende un array come lista
|
||||
// di argomenti.
|
||||
|
||||
anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!"
|
||||
|
||||
// Questo è utile quanso si lavora con una funzione che accetta una sequenza di
|
||||
// argomenti e si vuole passare un array.
|
||||
|
||||
Math.min(42, 6, 27); // = 6
|
||||
Math.min([42, 6, 27]); // = NaN (uh-oh!)
|
||||
Math.min.apply(Math, [42, 6, 27]); // = 6
|
||||
|
||||
// Ma, `call` e `apply` sono solo temporanei. Quando vogliamo incollarli, possiamo
|
||||
// usare `bind`
|
||||
|
||||
var boundFunc = anotherFunc.bind(myObj);
|
||||
boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!"
|
||||
|
||||
// `bind` può essere anche usato per applicare parzialmente (curry) una funzione.
|
||||
|
||||
var product = function(a, b){ return a * b; };
|
||||
var doubler = product.bind(this, 2);
|
||||
doubler(8); // = 16
|
||||
|
||||
// Quando si chiama una funzione con la parola chiave `new`, un nuovo oggetto viene creato
|
||||
// e reso disponibile alla funzione attraverso la parola chiave `this`. Le funzioni progettate per essere
|
||||
// invocate in questo modo sono chiamate costruttrici.
|
||||
|
||||
var MyConstructor = function(){
|
||||
this.myNumber = 5;
|
||||
};
|
||||
myNewObj = new MyConstructor(); // = {myNumber: 5}
|
||||
myNewObj.myNumber; // = 5
|
||||
|
||||
// Diversamente dalla molti degli altri linguaggi orientati agli oggetti, Javascript non ha
|
||||
// il concetto di 'istanze' create sull'impronta di una 'classe'; invece Javascript
|
||||
// combina l'instanziamento e l'ereditarietà in un singolo concetto: il 'prototipo'.
|
||||
|
||||
// Ogni oggetto Javascript ha un 'prototipo'. Quando si cerca di accedere a una proprietà
|
||||
// su un oggetto che non la contiene, l'interprete
|
||||
// guarderà i suoi prototipi.
|
||||
|
||||
// Alcune implementazioni di JS faranno accedere al propotipo di un oggetto con la proprietà
|
||||
// magica `__proto__`: Anche se questo è utile per spiegare i prototipi, non è
|
||||
// parte dello standard; capiremo più avanti come usare i prototipi in modo standard.
|
||||
var myObj = {
|
||||
myString: "Hello world!"
|
||||
};
|
||||
var myPrototype = {
|
||||
meaningOfLife: 42,
|
||||
myFunc: function(){
|
||||
return this.myString.toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
myObj.__proto__ = myPrototype;
|
||||
myObj.meaningOfLife; // = 42
|
||||
|
||||
// Questo funziona anche per le funzioni.
|
||||
myObj.myFunc(); // = "hello world!"
|
||||
|
||||
// Ovviamente, se la proprietà non è nel prototipo, il prototipo
|
||||
// del prototipo viene ricercato, e così via.
|
||||
myPrototype.__proto__ = {
|
||||
myBoolean: true
|
||||
};
|
||||
myObj.myBoolean; // = true
|
||||
|
||||
// Non c'è nessuna copia coinvolta qui; ogni oggetto mantiene una referenza al suo
|
||||
// prototipo. Questo significa che possiamo modificare il prototipo e i nostri cambiamenti
|
||||
// si rifletteranno da ogni parte.
|
||||
myPrototype.meaningOfLife = 43;
|
||||
myObj.meaningOfLife; // = 43
|
||||
|
||||
// L'istruzione for/in permette di iterare sulle proprietà di un oggetto,
|
||||
// risalendo la catena dei prototipi finché non trova un prototipo null.
|
||||
for (var x in myObj){
|
||||
console.log(myObj[x]);
|
||||
}
|
||||
///stampa:
|
||||
// Hello world!
|
||||
// 43
|
||||
// [Function: myFunc]
|
||||
// true
|
||||
|
||||
// Per considerare solamente le proprietà inserite nell'oggetto stesso
|
||||
// e non i loro prototipi, usare il check `hasOwnProperty()`.
|
||||
for (var x in myObj){
|
||||
if (myObj.hasOwnProperty(x)){
|
||||
console.log(myObj[x]);
|
||||
}
|
||||
}
|
||||
///stampa:
|
||||
// Hello world!
|
||||
|
||||
// Abbiamo menzionato che `__proto__` non è standard, e non c'è nessun modo standard per
|
||||
// cambiare il prototipo di un oggetto esistente. Tuttavia, ci sono due strade per
|
||||
// creare un nuovo oggetto con un dato prototipo.
|
||||
|
||||
// La prima è Object.create, che è una recente aggiunta a JS, e che quindi
|
||||
// non è disponibile ancora in tutte le implementazioni.
|
||||
var myObj = Object.create(myPrototype);
|
||||
myObj.meaningOfLife; // = 43
|
||||
|
||||
// La seconda strada, che funziona ovunque, ha a che fare con i costruttori.
|
||||
// I costruttori hanno una proprietà chiamata prototype. Questo *non* è il prototipo del
|
||||
// costruttore della stessa funzione; invece è il prototipo del nuovo oggetto
|
||||
// che gli viene conferito alla creazione con quel costruttore e la parola chiave new.
|
||||
MyConstructor.prototype = {
|
||||
myNumber: 5,
|
||||
getMyNumber: function(){
|
||||
return this.myNumber;
|
||||
}
|
||||
};
|
||||
var myNewObj2 = new MyConstructor();
|
||||
myNewObj2.getMyNumber(); // = 5
|
||||
myNewObj2.myNumber = 6;
|
||||
myNewObj2.getMyNumber(); // = 6
|
||||
|
||||
// I tipi built-in come stringhe e numeri hanno anche costruttori che creano
|
||||
// oggetti wrapper equivalenti.
|
||||
var myNumber = 12;
|
||||
var myNumberObj = new Number(12);
|
||||
myNumber == myNumberObj; // = true
|
||||
|
||||
// Eccezione, loro non sono esattamente equivalenti.
|
||||
typeof myNumber; // = 'number'
|
||||
typeof myNumberObj; // = 'object'
|
||||
myNumber === myNumberObj; // = false
|
||||
if (0){
|
||||
// Questo codice non verrà eseguito perchè 0 è falso.
|
||||
}
|
||||
if (new Number(0)){
|
||||
// Questo codice verrà eseguito poiché i numeri wrappati sono oggetti e gli oggetti
|
||||
// sono sempre veri.
|
||||
}
|
||||
|
||||
// Tuttavia, gli oggetti wrapper e i regolari built-in condividono un prototipo, quindi
|
||||
// si possono aggiungere funzionalità ad una stringa, per esempio.
|
||||
String.prototype.firstCharacter = function(){
|
||||
return this.charAt(0);
|
||||
};
|
||||
"abc".firstCharacter(); // = "a"
|
||||
|
||||
// Questa caratteristica viene spesso usata nel "polyfilling", che implementa nuovi
|
||||
// aspetti in un vecchio sottoinsieme di JavaScript, in modo che si possano
|
||||
// usare in vecchi ambienti come browser non aggiornati.
|
||||
|
||||
// Per esempio, abbiamo menzionato che Object.create non è disponibile in tutte le
|
||||
// implementazioni, ma possiamo ancora usarlo con questo polyfill:
|
||||
if (Object.create === undefined){ // non lo sovrascrive se esiste già
|
||||
Object.create = function(proto){
|
||||
// crea un costruttore temporaneo con il giusto prototipo
|
||||
var Constructor = function(){};
|
||||
Constructor.prototype = proto;
|
||||
// quindi lo usa per creare un nuovo, propriamente-prototipato oggetto
|
||||
return new Constructor();
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Approfondimenti
|
||||
|
||||
Il [Mozilla Developer Networ][1] fornisce una documentazione eccellente su come Javascript è utilizzato nei browsers. In più è un wiki, quindi si può imparare di più aiutando gli altri condividendo la propria conoscenza.
|
||||
|
||||
MDN's [A re-introduction to JavaScript][2] copre molti dei concetti qui trattati in maggiore dettaglio. Questa guida ha deliberatamente coperto solamente il linguaggio JavaScript; se volete sapere di più su come usare JavaScript in una pagina web, iniziate leggendo il [Document Object Model][3].
|
||||
|
||||
[Learn Javascript by Example and with Challenges][4] è una variante di questo referenziario con integrate delle sfide.
|
||||
|
||||
[Javascript Garden][5] è una guida approfondita di tutte le parti controintuitive del linguaggio.
|
||||
|
||||
[JavaScript: The Definitive Guide][6] è una guida classica e referenziario.
|
||||
|
||||
[Eloqunt Javascript][8] di Marijn Haverbeke è un ottimo libro/ebook JS con terminale annesso
|
||||
|
||||
[Javascript: The Right Way][10] è una guida dedicata all'introduzione dei nuovi sviluppatori a JavaScript e come aiuto agli sviluppatori esperti per imparare di più sulle best practice.
|
||||
|
||||
[Javascript:info][11] è un moderno tutorial su javascript che copre le basi (linguaggio principale e lavorazione con un browser) come anche argomenti avanzati con spiegazioni concise.
|
||||
|
||||
|
||||
In aggiunta ai contributori di questo articolo, alcuni contenuti sono adattati dal Louie Dinh's Python tutorial su questo sito, e da [JS Tutorial][7] sul Mozilla Developer Network.
|
||||
|
||||
|
||||
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript
|
||||
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript
|
||||
[3]: https://developer.mozilla.org/en-US/docs/Using_the_W3C_DOM_Level_1_Core
|
||||
[4]: http://www.learneroo.com/modules/64/nodes/350
|
||||
[5]: http://bonsaiden.github.io/JavaScript-Garden/
|
||||
[6]: http://www.amazon.com/gp/product/0596805527/
|
||||
[7]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript
|
||||
[8]: http://eloquentjavascript.net/
|
||||
[10]: http://jstherightway.org/
|
||||
[11]: https://javascript.info/
|
135
ja-jp/asciidoc.html.markdown
Normal file
135
ja-jp/asciidoc.html.markdown
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
language: asciidoc
|
||||
contributors:
|
||||
- ["Ryan Mavilia", "http://unoriginality.rocks/"]
|
||||
- ["Abel Salgado Romero", "https://twitter.com/abelsromero"]
|
||||
translators:
|
||||
- ["Ryota Kayanuma", "https://github.com/PicoSushi"]
|
||||
filename: asciidoc-ja.md
|
||||
lang: ja-jp
|
||||
---
|
||||
|
||||
AsciiDocはMarkdownに似たマークアップ言語で、書籍の執筆からブログを書くことまでなんでも使うことができます。2002年、Stuart RackhamによりAsciiDocは作成され、シンプルでありつつも沢山のカスタマイズを可能にしています。
|
||||
|
||||
文書のヘッダー
|
||||
|
||||
ヘッダーはオプションで、空行を含むことはできません。本文から1行以上の改行を開ける必要があります。
|
||||
|
||||
タイトルのみの例
|
||||
|
||||
```
|
||||
= 文章タイトル
|
||||
|
||||
文書の最初の行
|
||||
```
|
||||
|
||||
タイトルと著者
|
||||
|
||||
```
|
||||
= 文書タイトル
|
||||
文書 太郎 <first.last@learnxinyminutes.com>
|
||||
|
||||
文書の開始
|
||||
```
|
||||
|
||||
複数の著者
|
||||
|
||||
```
|
||||
= 文書タイトル
|
||||
John Doe <john@go.com>; Jane Doe<jane@yo.com>; Black Beard <beardy@pirate.com>
|
||||
|
||||
複数の著者による文書の始まり。
|
||||
```
|
||||
|
||||
版(著者の行を必要とします)
|
||||
|
||||
```
|
||||
= 第一版のタイトル
|
||||
芋男 <chip@crunchy.com>
|
||||
v1.0, 2016-01-13
|
||||
|
||||
このポテトについての文書は面白いです。
|
||||
```
|
||||
|
||||
段落
|
||||
|
||||
```
|
||||
段落は特別なことは不要です。
|
||||
|
||||
空行を段落の間に入れることで、段落を分けることができます。
|
||||
|
||||
折り返しをしたい場合、+
|
||||
を書くことで折り返せます!
|
||||
```
|
||||
|
||||
文書の整形
|
||||
|
||||
```
|
||||
_アンダースコアで斜体になります。_
|
||||
*アスタリスクで太字になります。*
|
||||
*_組み合わせると楽しい_*
|
||||
`バッククォートで固定幅になります。`
|
||||
`*太字の固定幅*`
|
||||
```
|
||||
|
||||
節タイトル
|
||||
|
||||
```
|
||||
= Level 0 (文書のヘッダーにのみ使用してください)
|
||||
|
||||
== Level 1 <h2>
|
||||
|
||||
=== Level 2 <h3>
|
||||
|
||||
==== Level 3 <h4>
|
||||
|
||||
===== Level 4 <h5>
|
||||
|
||||
```
|
||||
|
||||
リスト
|
||||
|
||||
箇条書きリストを作るには、アスタリスクを使用してください。
|
||||
|
||||
```
|
||||
* foo
|
||||
* bar
|
||||
* baz
|
||||
```
|
||||
|
||||
番号付きリストを作るには、ピリオドを使用してください。
|
||||
|
||||
```
|
||||
. item 1
|
||||
. item 2
|
||||
. item 3
|
||||
```
|
||||
|
||||
リストはアスタリスクやピリオドを追加することで5段階まで入れ子にできます。
|
||||
|
||||
```
|
||||
* foo 1
|
||||
** foo 2
|
||||
*** foo 3
|
||||
**** foo 4
|
||||
***** foo 5
|
||||
|
||||
. foo 1
|
||||
.. foo 2
|
||||
... foo 3
|
||||
.... foo 4
|
||||
..... foo 5
|
||||
```
|
||||
|
||||
## 補足資料
|
||||
|
||||
AsciiDocの文書を処理するツールは2種類あります。
|
||||
|
||||
1. [AsciiDoc](http://asciidoc.org/): オリジナルのPython実装で、Linuxで利用可能です。現在は開発されておらず、メンテナンスのみの状態です。
|
||||
2. [Asciidoctor](http://asciidoctor.org/): Rubyによる別実装で、JavaやJavascriptでも利用可能です。AsciiDocに新しい機能や出力形式を追加するため、現在活発に開発されています。
|
||||
|
||||
以下のリンクは `AsciiDoctor` 実装関連のものです。
|
||||
|
||||
* [Markdown - AsciiDoc syntax comparison](http://asciidoctor.org/docs/user-manual/#comparison-by-example): Common MarkdownとAsciidocの要素を並べて比較しています。
|
||||
* [Getting started](http://asciidoctor.org/docs/#get-started-with-asciidoctor): インストールから簡潔な文書を作るための簡単なガイドです。
|
||||
* [Asciidoctor User Manual](http://asciidoctor.org/docs/user-manual/): 文法のリファレンス、例、描画ツール、その他を含む完全なドキュメントです。
|
@ -6,13 +6,16 @@ contributors:
|
||||
- ["Andre Polykanine", "https://github.com/Oire"]
|
||||
- ["Zachary Ferguson", "http://github.com/zfergus2"]
|
||||
- ["evuez", "http://github.com/evuez"]
|
||||
- ["Rommel Martinez", "https://ebzzry.io"]
|
||||
- ["Roberto Fernandez Diaz", "https://github.com/robertofd1995"]
|
||||
translators:
|
||||
- ["kakakaya", "https://github.com/kakakaya"]
|
||||
- ["Ryota Kayanuma", "https://github.com/PicoSushi"]
|
||||
filename: learnpython3-jp.py
|
||||
lang: ja-jp
|
||||
---
|
||||
|
||||
90年代の初め、Guido Van RossumによってPythonは作成されました。現在となっては、最も有名な言語の1つです。
|
||||
90年代の初め、Guido van RossumによってPythonは作成されました。現在となっては、最も有名な言語の1つです。
|
||||
私は構文の明快さによって、Pythonと恋に落ちました。
|
||||
以下は基本的に実行可能な疑似コードです。
|
||||
|
||||
@ -21,12 +24,11 @@ lang: ja-jp
|
||||
Note: この記事はPython 3に内容を絞っています。もし古いPython 2.7を学習したいなら、 [こちら](http://learnxinyminutes.com/docs/python/) をご確認下さい。
|
||||
|
||||
```python
|
||||
|
||||
# 1行のコメントは番号記号(#)から始まります。
|
||||
|
||||
""" 複数行の文字は、"を3つ繋げることで
|
||||
書くことができます。
|
||||
また、これはコメントとしてもよく使われます。
|
||||
また、これはドキュメントとしてもよく使われます。
|
||||
"""
|
||||
|
||||
####################################################
|
||||
@ -44,8 +46,8 @@ Note: この記事はPython 3に内容を絞っています。もし古いPython
|
||||
|
||||
# 整数除算の結果は、正負に関わらず小数の切り捨てが行われます。
|
||||
5 // 3 # => 1
|
||||
5.0 // 3.0 # => 1.0 # 浮動小数点でも同様に動作します。
|
||||
-5 // 3 # => -2
|
||||
5.0 // 3.0 # => 1.0 # 浮動小数点でも同様に動作します。
|
||||
-5.0 // 3.0 # => -2.0
|
||||
|
||||
# 除算の結果は常に浮動小数点になります。
|
||||
@ -55,7 +57,7 @@ Note: この記事はPython 3に内容を絞っています。もし古いPython
|
||||
7 % 3 # => 1
|
||||
|
||||
# 冪乗 (x**y, x の y 乗)
|
||||
2**4 # => 16
|
||||
2**3 # => 8
|
||||
|
||||
# 括弧により、計算の順番を優先させられます。
|
||||
(1 + 3) * 2 # => 8
|
||||
@ -69,16 +71,28 @@ not True # => False
|
||||
not False # => True
|
||||
|
||||
# ブール演算
|
||||
# 注意: "and" と "or" は小文字です
|
||||
# 注意: "and" と "or" は小文字です。
|
||||
True and False # => False
|
||||
False or True # => True
|
||||
|
||||
# 整数でブール演算をするときのメモ
|
||||
# TrueとFalseは実際には1と0になるキーワードです。
|
||||
True + True # => 2
|
||||
True * 8 # => 8
|
||||
False - 5 # => -5
|
||||
|
||||
# 比較演算子はTrueとFalseを数値として扱います。
|
||||
0 == False # => True
|
||||
1 == True # => True
|
||||
2 == True # => False
|
||||
-5 != True # => True
|
||||
|
||||
# bool論理演算子を整数に対して使うことで整数を真偽値に変換して評価できますが、キャストされていない値が
|
||||
# bool(int)とビット演算子(& や |)を混同しないようにうにしましょう。
|
||||
bool(0) # => False
|
||||
bool(4) # => True
|
||||
bool(-6) # => True
|
||||
0 and 2 # => 0
|
||||
-5 or 0 # => -5
|
||||
0 == False # => True
|
||||
2 == True # => False
|
||||
1 == True # => True
|
||||
|
||||
# 値が等しいか確認するには ==
|
||||
1 == 1 # => True
|
||||
@ -94,7 +108,11 @@ False or True # => True
|
||||
2 <= 2 # => True
|
||||
2 >= 2 # => True
|
||||
|
||||
# 比較は連結させられます!
|
||||
# 値がある範囲の中にあるか調べる方法
|
||||
1 < 2 and 2 < 3 # => True
|
||||
2 < 3 and 3 < 2 # => False
|
||||
|
||||
# 連結させるともっと見やすくなります。
|
||||
1 < 2 < 3 # => True
|
||||
2 < 3 < 2 # => False
|
||||
|
||||
@ -115,7 +133,7 @@ b == a # => True, a と b が参照するオブジェクトの値
|
||||
|
||||
# 文字列も加算をすることができます!でも、あまり行わないように。
|
||||
"Hello " + "world!" # => "Hello world!"
|
||||
# '+' を使わなくても連結はできます。
|
||||
# '+' を使わなくても文字列リテラル(変数ではないもの)の連結ができます。
|
||||
"Hello " "world!" # => "Hello world!"
|
||||
|
||||
# 文字列は文字のリストであるかのように扱うことができます。
|
||||
@ -138,6 +156,12 @@ len("This is a string") # => 16
|
||||
# 旧式のフォーマット方法を使うこともできます。
|
||||
"%s can be %s the %s way" % ("Strings", "interpolated", "old") # => "Strings can be interpolated the old way"
|
||||
|
||||
# Python3.6以上では、f-stringsやフォーマット文字列を使ってフォーマットすることもできます。
|
||||
name = "Reiko"
|
||||
f"She said her name is {name}." # => "She said her name is Reiko"
|
||||
|
||||
# 基本的に、任意のPythonの文を中括弧に書くことができ、それは評価されて出力されます。
|
||||
f"{name} is {len(name)} characters long."
|
||||
|
||||
# None はオブジェクトです(大文字からです!)
|
||||
None # => None
|
||||
@ -170,7 +194,7 @@ print("Hello, World", end="!") # => Hello, World!
|
||||
input_string_var = input("Enter some data: ") # 入力を文字列として返します
|
||||
# Note: Python の初期のバージョンでは、 input() は raw_input() という名前で存在します。
|
||||
|
||||
# 変数に代入する前に宣言する必要はありません。
|
||||
# Pythonでは変数の宣言は存在せず、代入のみです。
|
||||
# 慣例的に、小文字でアンダースコア区切り ( lower_case_with_underscores ) の変数が使われます。
|
||||
some_var = 5
|
||||
some_var # => 5
|
||||
@ -207,10 +231,11 @@ li[-1] # => 3
|
||||
li[4] # IndexError が発生します
|
||||
|
||||
# スライス構文により範囲を参照できます。
|
||||
# 開始部分のインデックスに対応する部分は含まれますが、終了部分のインデックスに対応する部分は含まれません。
|
||||
li[1:3] # => [2, 4]
|
||||
# 先端を取り除く
|
||||
# 先端を取り除いたリスト
|
||||
li[2:] # => [4, 3]
|
||||
# 末尾を取り除く
|
||||
# 末尾を取り除いたリスト
|
||||
li[:3] # => [1, 2, 4]
|
||||
# 1つ飛ばしで選択する
|
||||
li[::2] # =>[1, 4]
|
||||
@ -272,7 +297,7 @@ a, b, c = (1, 2, 3) # a, b, c にはそれぞれ 1, 2, 3 が代入
|
||||
# 拡張記法もあります。
|
||||
a, *b, c = (1, 2, 3, 4) # a は 1 、 b は [2, 3] 、c は4 になります。
|
||||
# 括弧を作成しなくてもデフォルトでタプルが作成されます。
|
||||
d, e, f = 4, 5, 6
|
||||
d, e, f = 4, 5, 6 # 4、5、6がそれぞれd、 e、 fに代入されます。
|
||||
# 2つの変数を交換するのがどれほど簡単か見てみましょう。
|
||||
e, d = d, e # d は 5 、 e は e になります。
|
||||
|
||||
@ -293,14 +318,17 @@ filled_dict["one"] # => 1
|
||||
|
||||
# "keys()"により、全てのキーを反復可能な形式で取り出せます。
|
||||
# これをリストにするために、"list()"で囲んでいます。これについては後程解説します。
|
||||
# Note: 辞書のキーの順番は考慮されていません。実行した結果がこれと異なる場合があります。
|
||||
list(filled_dict.keys()) # => ["three", "two", "one"]
|
||||
# Note: Python3.7未満では、辞書のキーの順番は考慮されていません。実行した結果がこれと異なる場合があります。
|
||||
# しかし、Python3.7以降ではキーの挿入順を保つようになりました。
|
||||
list(filled_dict.keys()) # => ["three", "two", "one"] in Python <3.7
|
||||
list(filled_dict.keys()) # => ["one", "two", "three"] in Python 3.7+
|
||||
|
||||
|
||||
# "values()"により、全ての値を反復可能な形式で取り出せます。
|
||||
# 前と同じように、これをリストにするために、"list()"で囲んでいます。
|
||||
# Note: 辞書の値の順番は考慮されていません。実行した結果がこれと異なる場合があります。
|
||||
list(filled_dict.values()) # => [3, 2, 1]
|
||||
|
||||
list(filled_dict.values()) # => [3, 2, 1] in Python <3.7
|
||||
list(filled_dict.values()) # => [1, 2, 3] in Python 3.7+
|
||||
|
||||
# "in" により、辞書のキーが存在するか確認できます。
|
||||
"one" in filled_dict # => True
|
||||
@ -322,7 +350,7 @@ filled_dict.setdefault("five", 6) # filled_dict["five"] は 5 のままです
|
||||
|
||||
# 辞書にマップを追加する
|
||||
filled_dict.update({"four": 4}) # => {"one": 1, "two": 2, "three": 3, "four": 4}
|
||||
# filled_dict["four"] = 4 # 辞書に追加する別の方法
|
||||
filled_dict["four"] = 4 # 辞書に追加する別の方法
|
||||
|
||||
# del により辞書からキーを削除できます。
|
||||
del filled_dict["one"] # "one" キーを辞書から削除します。
|
||||
@ -341,11 +369,11 @@ some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4}
|
||||
invalid_set = {[1], 1} # => list はハッシュ化できないので、 TypeError が送出されます。
|
||||
valid_set = {(1,), 1}
|
||||
|
||||
# 新しい値を集合にセットできます。
|
||||
filled_set = some_set
|
||||
|
||||
# 集合に新しい要素を追加できます。
|
||||
filled_set = some_set
|
||||
filled_set.add(5) # filled_set は {1, 2, 3, 4, 5} になりました。
|
||||
# 集合は重複した要素を持ちません。
|
||||
filled_set.add(5) # 以前の{1, 2, 3, 4, 5}のままです。
|
||||
|
||||
# & により、集合同士の共通部分が得られます。
|
||||
other_set = {3, 4, 5, 6}
|
||||
@ -378,7 +406,8 @@ filled_set | other_set # => {1, 2, 3, 4, 5, 6}
|
||||
# まずは変数を作りましょう。
|
||||
some_var = 5
|
||||
|
||||
# これはif文です。インデントがPythonでは特徴的ですね!
|
||||
# これはif文です。Pythonではインデントが特徴的ですね!
|
||||
# 規約ではタブではなく4つのスペースでインデントすることが推奨されています。
|
||||
# 以下の例では"some_var is smaller than 10"と出力されます。
|
||||
if some_var > 10:
|
||||
print("some_var is totally bigger than 10.")
|
||||
@ -541,9 +570,9 @@ all_the_args(1, 2, a=3, b=4) prints:
|
||||
# * を使ってタプルを展開したり、 ** を使って辞書を展開できます。
|
||||
args = (1, 2, 3, 4)
|
||||
kwargs = {"a": 3, "b": 4}
|
||||
all_the_args(*args) # foo(1, 2, 3, 4) に対応します。
|
||||
all_the_args(**kwargs) # foo(a=3, b=4) に対応します。
|
||||
all_the_args(*args, **kwargs) # foo(1, 2, 3, 4, a=3, b=4) に対応します。
|
||||
all_the_args(*args) # all_the_args(1, 2, 3, 4) と等しいです。
|
||||
all_the_args(**kwargs) # all_the_args(a=3, b=4) と等しいです。
|
||||
all_the_args(*args, **kwargs) # all_the_args(1, 2, 3, 4, a=3, b=4) と等しいです。
|
||||
|
||||
|
||||
# タプルで複数の値を返す
|
||||
@ -646,7 +675,7 @@ dir(math)
|
||||
# 6. クラス
|
||||
####################################################
|
||||
|
||||
# クラスを作成するために、"class"という演算子を使います。
|
||||
# クラスを作成するために、class文を使います。
|
||||
class Human:
|
||||
|
||||
# クラスの属性です。このクラスの全てのインスタンスで共有されます。
|
||||
@ -656,14 +685,14 @@ class Human:
|
||||
# 2つのアンダースコアがオブジェクトや属性の前後についているとき、これらはPythonによって利用され、
|
||||
# ユーザーの名前空間には存在しないということに注意してください。
|
||||
# __init__ や __str__ 、 __repr__ のようなメソッド(やオブジェクト、属性)は、
|
||||
# magic methods (または dunder methods)と呼ばれます。
|
||||
# このような名前を自分で発明しないほうがよいでしょう。
|
||||
# special methods (または dunder methods)と呼ばれます。
|
||||
# 同じような名前を自分で発明しないほうがよいでしょう。
|
||||
def __init__(self, name):
|
||||
# 引数をインスタンスのname属性に設定します。
|
||||
self.name = name
|
||||
|
||||
# プロパティの初期化
|
||||
self.age = 0
|
||||
self._age = 0
|
||||
|
||||
# インスタンスメソッド。全てのメソッドは"self"を最初の引数に取ります。
|
||||
def say(self, msg):
|
||||
@ -686,6 +715,7 @@ class Human:
|
||||
|
||||
# プロパティはgetterのようなものです。
|
||||
# age() メソッドを同名の読取専用属性に変換します。
|
||||
# Pythonではわざわざgetterやsetterを書く必要はありません。
|
||||
@property
|
||||
def age(self):
|
||||
return self._age
|
||||
@ -720,24 +750,117 @@ if __name__ == '__main__':
|
||||
j.say(j.get_species()) # => "Joel: H. neanderthalensis"
|
||||
|
||||
# スタティックメソッドを呼んでみましょう。
|
||||
print(Human.grunt()) # => "*grunt*"
|
||||
print(i.grunt()) # => "*grunt*"
|
||||
print(Human.grunt()) # => "*grunt*"
|
||||
|
||||
# スタティックメソッドはインスタンスから呼ぶことはできません。
|
||||
# なぜならば、 i.grunt() は自動的に"self" ( i オブジェクト ) を引数として渡してしまうからです。
|
||||
print(i.grunt()) # => TypeError: grunt() takes 0 positional arguments but 1 was given
|
||||
|
||||
# インスタンスのプロパティを更新してみましょう。
|
||||
i.age = 42
|
||||
# プロパティを取得してみましょう。
|
||||
i.say(i.age) # => 42
|
||||
j.say(j.age) # => 0
|
||||
i.say(i.age) # => "Ian: 42"
|
||||
j.say(j.age) # => "Joel: 0"
|
||||
# プロパティを削除してみましょう。
|
||||
del i.age
|
||||
# i.age # => AttributeError が発生します。
|
||||
|
||||
|
||||
####################################################
|
||||
# 6.1 多重継承
|
||||
# 6.1 継承
|
||||
####################################################
|
||||
# 継承を行うことで、親クラスからメソッドと変数を継承する新しい子クラスを定義できます。
|
||||
|
||||
# 上記で定義されたHumanクラスを親クラス(基底クラス)として使い、Superheroという子クラスを定義します。
|
||||
# これは"species"、"name"や"age"といった変数や、"sing"や"grunt"のようなメソッドをHumanから継承しますが、
|
||||
# Superhero独自のプロパティを持つこともできます。
|
||||
|
||||
# ファイルを分割してモジュール化の利点を活用するために、上記のHumanクラスを独自のファイル、ここでは human.py に記述ましょう。
|
||||
|
||||
# 別のファイルから関数をインポートするには次の形式を利用してください:
|
||||
# from "拡張子なしのファイル名" import "関数やクラス"
|
||||
|
||||
from human import Human
|
||||
|
||||
|
||||
# 親クラスを子クラスのパラメータとして指定します
|
||||
class Superhero(Human):
|
||||
|
||||
# もし子クラスが親クラスの全ての定義を変更なしで継承する場合、"pass"キーワードのみを書くだけで良いです。
|
||||
# しかし、今回は親クラスとは異なる子クラスを作成するので、今回は以下の通りコメントアウトしています。
|
||||
# pass
|
||||
|
||||
# 子クラスは親クラスの属性を上書きできます。
|
||||
species = 'Superhuman'
|
||||
|
||||
# 子クラスは親クラスのコンストラクタを引数含めて自動的に継承しますが、
|
||||
# 追加の引数や定義を行ってコンストラクタのようなメソッドを上書きすることもできます。
|
||||
# このコンストラクタは"name"引数を"Human"クラスから継承し、"superpower"と"movie"という引数を追加します。
|
||||
def __init__(self, name, movie=False,
|
||||
superpowers=["super strength", "bulletproofing"]):
|
||||
|
||||
# 追加のクラス属性を作成する
|
||||
self.fictional = True
|
||||
self.movie = movie
|
||||
# デフォルト値は共有されるので、可変のデフォルト値には注意してください。
|
||||
self.superpowers = superpowers
|
||||
|
||||
# "super"関数を使うと子クラスに上書きされた親クラスのメソッド(今回は "__init__")にアクセスできます。
|
||||
# これで、親クラスのコンストラクタを呼んでいます。
|
||||
super().__init__(name)
|
||||
|
||||
# singメソッドを上書きし、
|
||||
def sing(self):
|
||||
return 'Dun, dun, DUN!'
|
||||
|
||||
# 追加のインスタンスメソッドを作成します。
|
||||
def boast(self):
|
||||
for power in self.superpowers:
|
||||
print("I wield the power of {pow}!".format(pow=power))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sup = Superhero(name="Tick")
|
||||
|
||||
# インスタンスの型を調べる
|
||||
if isinstance(sup, Human):
|
||||
print('I am human')
|
||||
if type(sup) is Superhero:
|
||||
print('I am a superhero')
|
||||
|
||||
# getattr()とsuper()で使われるメソッドの解決順序を調べてみます。
|
||||
# この属性は動的であり、変更可能です。
|
||||
print(Superhero.__mro__) # => (<class '__main__.Superhero'>,
|
||||
# => <class 'human.Human'>, <class 'object'>)
|
||||
|
||||
# 親のメソッドを呼びだすものの、独自のクラス属性を参照します。
|
||||
print(sup.get_species()) # => Superhuman
|
||||
|
||||
# 上書きされたメソッドを呼ぶ
|
||||
print(sup.sing()) # => Dun, dun, DUN!
|
||||
|
||||
# Humanのメソッドを呼ぶ
|
||||
sup.say('Spoon') # => Tick: Spoon
|
||||
|
||||
# Superhero限定のメソッドを呼ぶ
|
||||
sup.boast() # => I wield the power of super strength!
|
||||
# => I wield the power of bulletproofing!
|
||||
|
||||
# 継承されたクラス属性
|
||||
sup.age = 31
|
||||
print(sup.age) # => 31
|
||||
|
||||
# Superhero限定の属性
|
||||
print('Am I Oscar eligible? ' + str(sup.movie))
|
||||
|
||||
|
||||
|
||||
####################################################
|
||||
# 6.2 多重継承
|
||||
####################################################
|
||||
|
||||
# 別のクラスを定義します。
|
||||
# bat.py
|
||||
class Bat:
|
||||
|
||||
species = 'Baty'
|
||||
@ -759,31 +882,22 @@ if __name__ == '__main__':
|
||||
print(b.say('hello'))
|
||||
print(b.fly)
|
||||
|
||||
# ファイル単位のモジュール化を利用するために、上記のクラスを別々のファイルに配置することができます。
|
||||
# ここでは、human.pyとbat.pyを作成してみましょう。
|
||||
|
||||
# 他のファイルから関数をインポートするために、次のような形式を利用してください。
|
||||
# from "拡張子無しのファイル名" import "関数またはクラス"
|
||||
|
||||
# superhero.py
|
||||
from human import Human
|
||||
from superhero import Superhero
|
||||
from bat import Bat
|
||||
|
||||
|
||||
# BatmanはHumanとBatの両方を継承します。
|
||||
class Batman(Human, Bat):
|
||||
|
||||
# Batmanは species のクラス属性に独自の値を持ちます。
|
||||
species = 'Superhero'
|
||||
# BatmanをSuperheroとBatの両方を継承した子クラスとして定義します。
|
||||
class Batman(Superhero, Bat):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# 通常、属性を継承するにはsuper()を呼び出します。
|
||||
# super(Batman, self).__init__(*args, **kwargs)
|
||||
# super(Batman, self).__init__(*args, **kwargs)
|
||||
# しかし、ここでは多重継承を行っているので、 super() はMRO(メソッド解決順序)の次の基本クラスにのみ動作します。
|
||||
# なので、全ての祖先に対して明示的に __init__ を呼ぶことにします。
|
||||
# *args と **kwargs を使うことで、それぞれの継承元が
|
||||
# たまねぎの皮を剥がすごとく、引数を用いることができます。
|
||||
Human.__init__(self, 'anonymous', *args, **kwargs)
|
||||
Superhero.__init__(self, 'anonymous', movie=True,
|
||||
superpowers=['Wealthy'], *args, **kwargs)
|
||||
Bat.__init__(self, *args, can_fly=False, **kwargs)
|
||||
# 名前の属性の値を上書きします。
|
||||
self.name = 'Sad Affleck'
|
||||
@ -795,22 +909,18 @@ class Batman(Human, Bat):
|
||||
if __name__ == '__main__':
|
||||
sup = Batman()
|
||||
|
||||
# インスタンスの型を調べてみましょう。
|
||||
if isinstance(sup, Human):
|
||||
print('I am human')
|
||||
if isinstance(sup, Bat):
|
||||
print('I am bat')
|
||||
if type(sup) is Batman:
|
||||
print('I am Batman')
|
||||
|
||||
# getattr() や super() の両方で使われるMROを取得します。
|
||||
# この属性は動的であり、更新が可能です。
|
||||
print(Batman.__mro__) # => (<class '__main__.Batman'>, <class 'human.Human'>, <class 'bat.Bat'>, <class 'object'>)
|
||||
print(Batman.__mro__) # => (<class '__main__.Batman'>,
|
||||
# => <class 'superhero.Superhero'>,
|
||||
# => <class 'human.Human'>,
|
||||
# => <class 'bat.Bat'>, <class 'object'>)
|
||||
|
||||
|
||||
# 親メソッドを呼び出しますが、独自のクラス属性を参照します。
|
||||
print(sup.get_species()) # => Superhero
|
||||
print(sup.get_species()) # => Superhuman
|
||||
|
||||
# オーバーロードされたメソッドを呼び出します。
|
||||
# 上書きされたメソッドを呼び出します。
|
||||
print(sup.sing()) # => nan nan nan nan nan batman!
|
||||
|
||||
# 継承順により、Humanから継承されたメソッドを呼び出します。
|
||||
@ -821,10 +931,10 @@ if __name__ == '__main__':
|
||||
|
||||
# 継承されたクラス属性
|
||||
sup.age = 100
|
||||
print(sup.age)
|
||||
print(sup.age) # => 100
|
||||
|
||||
# デフォルト値が上書きされて、2番目の先祖から継承された属性
|
||||
print('Can I fly? ' + str(sup.fly))
|
||||
print('Can I fly? ' + str(sup.fly)) # => Can I fly? False
|
||||
|
||||
|
||||
####################################################
|
||||
|
@ -350,11 +350,22 @@ fun helloWorld(val name : String) {
|
||||
|
||||
// Enum classes are similar to Java enum types.
|
||||
enum class EnumExample {
|
||||
A, B, C
|
||||
A, B, C // Enum constants are separated with commas.
|
||||
}
|
||||
|
||||
fun printEnum() = println(EnumExample.A) // => A
|
||||
|
||||
// Since each enum is an instance of the enum class, they can be initialized as:
|
||||
enum class EnumExample(val value: Int) {
|
||||
A(value = 1),
|
||||
B(value = 2),
|
||||
C(value = 3)
|
||||
}
|
||||
fun printProperty() = println(EnumExample.A.value) // => 1
|
||||
|
||||
// Every enum has properties to obtain its name and ordinal(position) in the enum class declaration:
|
||||
fun printName() = println(EnumExample.A.name) // => A
|
||||
fun printPosition() = println(EnumExample.A.ordinal) // => 0
|
||||
|
||||
/*
|
||||
The "object" keyword can be used to create singleton objects.
|
||||
We cannot instantiate it but we can refer to its unique instance by its name.
|
||||
|
9
linker.html.markdown
Normal file
9
linker.html.markdown
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
category: tool
|
||||
tool: linker
|
||||
contributors:
|
||||
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
|
||||
---
|
||||
|
||||
This article is available in [Russian](http://localhost:4567/docs/ru-ru/linker-ru/).
|
||||
|
765
opengl.html.markdown
Normal file
765
opengl.html.markdown
Normal file
@ -0,0 +1,765 @@
|
||||
---
|
||||
category: tool
|
||||
tool: OpenGL
|
||||
filename: learnopengl.cpp
|
||||
contributors:
|
||||
- ["Simon Deitermann", "s.f.deitermann@t-online.de"]
|
||||
---
|
||||
|
||||
**Open Graphics Library** (**OpenGL**) is a cross-language cross-platform application programming interface
|
||||
(API) for rendering 2D computer graphics and 3D vector graphics.<sup>[1]</sup> In this tutorial we will be
|
||||
focusing on modern OpenGL from 3.3 and above, ignoring "immediate-mode", Displaylists and
|
||||
VBO's without use of Shaders.
|
||||
I will be using C++ with SFML for window, image and context creation aswell as GLEW
|
||||
for modern OpenGL extensions, though there are many other librarys available.
|
||||
|
||||
```cpp
|
||||
// Creating an SFML window and OpenGL basic setup.
|
||||
#include <GL/glew.h>
|
||||
#include <GL/gl.h>
|
||||
#include <SFML/Graphics.h>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
// First we tell SFML how to setup our OpenGL context.
|
||||
sf::ContextSettings context{ 24, // depth buffer bits
|
||||
8, // stencil buffer bits
|
||||
4, // MSAA samples
|
||||
3, // major opengl version
|
||||
3 }; // minor opengl version
|
||||
// Now we create the window, enable VSync
|
||||
// and set the window active for OpenGL.
|
||||
sf::Window window{ sf::VideoMode{ 1024, 768 },
|
||||
"opengl window",
|
||||
sf::Style::Default,
|
||||
context };
|
||||
window.setVerticalSyncEnabled(true);
|
||||
window.setActive(true);
|
||||
// After that we initialise GLEW and check if an error occured.
|
||||
GLenum error;
|
||||
glewExperimental = GL_TRUE;
|
||||
if ((err = glewInit()) != GLEW_OK)
|
||||
std::cout << glewGetErrorString(err) << std::endl;
|
||||
// Here we set the color glClear will clear the buffers with.
|
||||
glClearColor(0.0f, // red
|
||||
0.0f, // green
|
||||
0.0f, // blue
|
||||
1.0f); // alpha
|
||||
// Now we can start the event loop, poll for events and draw objects.
|
||||
sf::Event event{ };
|
||||
while (window.isOpen()) {
|
||||
while (window.pollEvent(event)) {
|
||||
if (event.type == sf::Event::Closed)
|
||||
window.close;
|
||||
}
|
||||
// Tell OpenGL to clear the color buffer
|
||||
// and the depth buffer, this will clear our window.
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
// Flip front- and backbuffer.
|
||||
window.display();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Loading Shaders
|
||||
|
||||
After creating a window and our event loop we should create a function,
|
||||
that sets up our shader program.
|
||||
|
||||
```cpp
|
||||
GLuint createShaderProgram(const std::string& vertexShaderPath,
|
||||
const std::string& fragmentShaderPath) {
|
||||
// Load the vertex shader source.
|
||||
std::stringstream ss{ };
|
||||
std::string vertexShaderSource{ };
|
||||
std::string fragmentShaderSource{ };
|
||||
std::ifstream file{ vertexShaderPath };
|
||||
if (file.is_open()) {
|
||||
ss << file.rdbuf();
|
||||
vertexShaderSource = ss.str();
|
||||
file.close();
|
||||
}
|
||||
// Clear the stringstream and load the fragment shader source.
|
||||
ss.str(std::string{ });
|
||||
file.open(fragmentShaderPath);
|
||||
if (file.is_open()) {
|
||||
ss << file.rdbuf();
|
||||
fragmentShaderSource = ss.str();
|
||||
file.close();
|
||||
}
|
||||
// Create the program.
|
||||
GLuint program = glCreateProgram();
|
||||
// Create the shaders.
|
||||
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
// Now we can load the shader source into the shader objects and compile them.
|
||||
// Because glShaderSource() wants a const char* const*,
|
||||
// we must first create a const char* and then pass the reference.
|
||||
const char* cVertexSource = vertexShaderSource.c_str();
|
||||
glShaderSource(vertexShader, // shader
|
||||
1, // number of strings
|
||||
&cVertexSource, // strings
|
||||
nullptr); // length of strings (nullptr for 1)
|
||||
glCompileShader(vertexShader);
|
||||
// Now we have to do the same for the fragment shader.
|
||||
const char* cFragmentSource = fragmentShaderSource.c_str();
|
||||
glShaderSource(fragmentShader, 1, &cFragmentSource, nullptr);
|
||||
glCompileShader(fragmentShader);
|
||||
// After attaching the source and compiling the shaders,
|
||||
// we attach them to the program;
|
||||
glAttachShader(program, vertexShader);
|
||||
glAttachShader(program, fragmentShader);
|
||||
glLinkProgram(program);
|
||||
// After linking the shaders we should detach and delete
|
||||
// them to prevent memory leak.
|
||||
glDetachShader(program, vertexShader);
|
||||
glDetachShader(program, fragmentShader);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
// With everything done we can return the completed program.
|
||||
return program;
|
||||
}
|
||||
```
|
||||
|
||||
If you want to check the compilation log you can add the following between <code>glCompileShader()</code> and <code>glAttachShader()</code>.
|
||||
|
||||
```cpp
|
||||
GLint logSize = 0;
|
||||
std::vector<GLchar> logText{ };
|
||||
glGetShaderiv(vertexShader, // shader
|
||||
GL_INFO_LOG_LENGTH, // requested parameter
|
||||
&logSize); // return object
|
||||
if (logSize > 0) {
|
||||
logText.resize(logSize);
|
||||
glGetShaderInfoLog(vertexShader, // shader
|
||||
logSize, // buffer length
|
||||
&logSize, // returned length
|
||||
logText.data()); // buffer
|
||||
std::cout << logText.data() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
The same is possibile after <code>glLinkProgram()</code>, just replace <code>glGetShaderiv()</code> with <code>glGetProgramiv()</code>
|
||||
and <code>glGetShaderInfoLog()</code> with <code>glGetProgramInfoLog()</code>.
|
||||
|
||||
```cpp
|
||||
// Now we can create a shader program with a vertex and a fragment shader.
|
||||
// ...
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
GLuint program = createShaderProgram("vertex.glsl", "fragment.glsl");
|
||||
|
||||
sf::Event event{ };
|
||||
// ...
|
||||
// We also have to delete the program at the end of the application.
|
||||
// ...
|
||||
}
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
Ofcourse we have to create the vertex and fragment shader before we can load them,
|
||||
so lets create two basic shaders.
|
||||
|
||||
**Vertex Shader**
|
||||
|
||||
```glsl
|
||||
// Declare which version of GLSL we use.
|
||||
// Here we declare, that we want to use the OpenGL 3.3 version of GLSL.
|
||||
#version 330 core
|
||||
// At attribute location 0 we want an input variable of type vec3,
|
||||
// that contains the position of the vertex.
|
||||
// Setting the location is optional, if you don't set it you can ask for the
|
||||
// location with glGetAttribLocation().
|
||||
layout(location = 0) in vec3 position;
|
||||
// Every shader starts in it's main function.
|
||||
void main() {
|
||||
// gl_Position is a predefined variable that holds
|
||||
// the final vertex position.
|
||||
// It consists of a x, y, z and w coordinate.
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
**Fragment Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
// The fragment shader does not have a predefined variable for
|
||||
// the vertex color, so we have to define a output vec4,
|
||||
// that holds the final vertex color.
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
// We simply set the ouput color to red.
|
||||
// The parameters are red, green, blue and alpha.
|
||||
outColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
## VAO and VBO
|
||||
Now we need to define some vertex position we can pass to our shaders. Lets define a simple 2D quad.
|
||||
|
||||
```cpp
|
||||
// The vertex data is defined in a counter-clockwise way,
|
||||
// as this is the default front face.
|
||||
std::vector<float> vertexData {
|
||||
-0.5f, 0.5f, 0.0f,
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f,
|
||||
0.5f, 0.5f, 0.0f
|
||||
};
|
||||
// If you want to use a clockwise definition, you can simply call
|
||||
glFrontFace(GL_CW);
|
||||
// Next we need to define a Vertex Array Object (VAO).
|
||||
// The VAO stores the current state while its active.
|
||||
GLuint vao = 0;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
// With the VAO active we can now create a Vertex Buffer Object (VBO).
|
||||
// The VBO stores our vertex data.
|
||||
GLuint vbo = 0;
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
// For reading and copying there are also GL_*_READ and GL_*_COPY,
|
||||
// if your data changes more often use GL_DYNAMIC_* or GL_STREAM_*.
|
||||
glBufferData(GL_ARRAY_BUFFER, // target buffer
|
||||
sizeof(vertexData[0]) * vertexData.size(), // size
|
||||
vertexData.data(), // data
|
||||
GL_STATIC_DRAW); // usage
|
||||
// After filling the VBO link it to the location 0 in our vertex shader,
|
||||
// which holds the vertex position.
|
||||
// ...
|
||||
// To ask for the attibute location, if you haven't set it:
|
||||
GLint posLocation = glGetAttribLocation(program, "position");
|
||||
// ..
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, // location and size
|
||||
GL_FLOAT, // type of data
|
||||
GL_FALSE, // normalized (always false for floats)
|
||||
0, // stride (interleaved arrays)
|
||||
nullptr); // offset (interleaved arrays)
|
||||
// Everything should now be saved in our VAO and we can unbind it and the VBO.
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
// Now we can draw the vertex data in our render loop.
|
||||
// ...
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// Tell OpenGL we want to use our shader program.
|
||||
glUseProgram(program);
|
||||
// Binding the VAO loads the data we need.
|
||||
glBindVertexArray(vao);
|
||||
// We want to draw a quad starting at index 0 of the VBO using 4 indices.
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
window.display();
|
||||
// ...
|
||||
// Ofcource we have to delete the allocated memory for the VAO and VBO at
|
||||
// the end of our application.
|
||||
// ...
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
// ...
|
||||
```
|
||||
|
||||
You can find the current code here: [OpenGL - 1](https://pastebin.com/W8jdmVHD).
|
||||
|
||||
## More VBO's and Color
|
||||
Let's create another VBO for some colors.
|
||||
|
||||
```cpp
|
||||
std::vector<float> colorData {
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 0.0f
|
||||
};
|
||||
```
|
||||
|
||||
Next we can simply change some previous parameters to create a second VBO for our colors.
|
||||
|
||||
```cpp
|
||||
// ...
|
||||
GLuint vbo[2];
|
||||
glGenBuffers(2, vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||
// ...
|
||||
glDeleteBuffers(2, vbo);
|
||||
/ ...
|
||||
// With these changes made we now have to load our color data into the new VBO
|
||||
// ...
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(colorData[0]) * colorData.size(),
|
||||
colorData.data(), GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
|
||||
glBindVertexArray(0);
|
||||
// ...
|
||||
```
|
||||
|
||||
Next we have to change our vertex shader to pass the color data to the fragment shader.<br>
|
||||
**Vertex Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
// The new location has to differ from any other input variable.
|
||||
// It is the same index we need to pass to
|
||||
// glEnableVertexAttribArray() and glVertexAttribPointer().
|
||||
layout(location = 1) in vec3 color;
|
||||
|
||||
out vec3 fColor;
|
||||
|
||||
void main() {
|
||||
fColor = color;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
**Fragment Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
|
||||
in vec3 fColor;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fColor, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
We define a new input variable ```color``` which represents our color data, this data
|
||||
is passed on to ```fColor```, which is an output variable of our vertex shader and
|
||||
becomes an input variable for our fragment shader.
|
||||
It is imporatant that variables passed between shaders have the exact same name
|
||||
and type.
|
||||
|
||||
## Handling VBO's
|
||||
|
||||
```cpp
|
||||
// If you want to completely clear and refill a VBO use glBufferData(),
|
||||
// just like we did before.
|
||||
// ...
|
||||
// There are two mains ways to update a subset of a VBO's data.
|
||||
// To update a VBO with existing data
|
||||
std::vector<float> newSubData {
|
||||
-0.25f, 0.5f, 0.0f
|
||||
};
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, // target buffer
|
||||
0, // offset
|
||||
sizeof(newSubData[0]) * newSubData.size(), // size
|
||||
newSubData.data()); // data
|
||||
// This would update the first three values in our vbo[0] buffer.
|
||||
// If you want to update starting at a specific location just set the second
|
||||
// parameter to that value and multiply by the types size.
|
||||
// ...
|
||||
// If you are streaming data, for example from a file,
|
||||
// it is faster to directly pass the data to the buffer.
|
||||
// Other access values are GL_READ_ONLY and GL_READ_WRITE.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||
// You can static_cast<float*>() the void* to be more safe.
|
||||
void* Ptr = glMapBuffer(GL_ARRAY_BUFFER, // buffer to map
|
||||
GL_WRITE_ONLY); // access to buffer
|
||||
memcpy(Ptr, newSubData.data(), sizeof(newSubData[0]) * newSubData.size());
|
||||
// To copy to a specific location add a destination offset to memcpy().
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
// ...
|
||||
// There is also a way to copy data from one buffer to another,
|
||||
// If we have two VBO's vbo[0] and vbo[1], we can copy like so
|
||||
// You can also read from GL_ARRAY_BUFFER.
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, vbo[0]);
|
||||
// GL_COPY_READ_BUFFER and GL_COPY_WRITE_BUFFER are specifically for
|
||||
// copying buffer data.
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo[1]);
|
||||
glCopyBufferSubData(GL_COPY_READ_BUFFER, // read buffer
|
||||
GL_COPY_WRITE_BUFFER, // write buffer
|
||||
0, 0, // read and write offset
|
||||
sizeof(vbo[0]) * 3); // copy size
|
||||
// This will copy the first three elements from vbo[0] to vbo[1].
|
||||
```
|
||||
|
||||
## Uniforms
|
||||
|
||||
**Fragment Shader**
|
||||
|
||||
```glsl
|
||||
// Uniforms are variables like in and out, however,
|
||||
// we can change them easily by passing new values with glUniform().
|
||||
// Lets define a time variable in our fragment shader.
|
||||
#version 330 core
|
||||
// Unlike a in/out variable we can use a uniform in every shader,
|
||||
// without the need to pass it to the next one, they are global.
|
||||
// Don't use locations already used for attributes!
|
||||
// Uniform layout locations require OpenGL 4.3!
|
||||
layout(location = 10) uniform float time;
|
||||
|
||||
in vec3 fColor;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
// Create a sine wave from 0 to 1 based on the time passed to the shader.
|
||||
float factor = (sin(time * 2) + 1) / 2;
|
||||
outColor = vec4(fColor.r * factor, fColor.g * factor, fColor.b * factor, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
Back to our source code.
|
||||
|
||||
```cpp
|
||||
// If we haven't set the layout location, we can ask for it.
|
||||
GLint timeLocation = glGetUniformLocation(program, "time");
|
||||
// ...
|
||||
// Also we should define a Timer counting the current time.
|
||||
sf::Clock clock{ };
|
||||
// In out render loop we can now update the uniform every frame.
|
||||
// ...
|
||||
window.display();
|
||||
glUniform1f(10, // location
|
||||
clock.getElapsedTime().asSeconds()); // data
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
With the time getting updated every frame the quad should now be changing from
|
||||
fully colored to pitch black.
|
||||
There are different types of glUniform() you can find simple documentation here:
|
||||
[glUniform - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUniform.xhtml)
|
||||
|
||||
## Indexing and IBO's
|
||||
|
||||
Element Array Buffers or more commonly Index Buffer Objects (IBO) allow us to use the
|
||||
same vertex data again which makes drawing a lot easier and faster. here's an example:
|
||||
|
||||
```cpp
|
||||
// Lets create a quad from two rectangles.
|
||||
// We can simply use the old vertex data from before.
|
||||
// First, we have to create the IBO.
|
||||
// The index is referring to the first declaration in the VBO.
|
||||
std::vector<unsigned int> iboData {
|
||||
0, 1, 2,
|
||||
0, 2, 3
|
||||
};
|
||||
// That's it, as you can see we could reuse 0 - the top left
|
||||
// and 2 - the bottom right.
|
||||
// Now that we have our data, we have to fill it into a buffer.
|
||||
// Note that this has to happen between the two glBindVertexArray() calls,
|
||||
// so it gets saved into the VAO.
|
||||
GLuint ibo = 0;
|
||||
glGenBufferrs(1, &ibo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(iboData[0]) * iboData.size(),
|
||||
iboData.data(), GL_STATIC_DRAW);
|
||||
// Next in our render loop, we replace glDrawArrays() with:
|
||||
glDrawElements(GL_TRIANGLES, iboData.size(), GL_UNSINGED_INT, nullptr);
|
||||
// Remember to delete the allocated memory for the IBO.
|
||||
```
|
||||
|
||||
You can find the current code here: [OpenGL - 2](https://pastebin.com/R3Z9ACDE).
|
||||
|
||||
## Textures
|
||||
|
||||
To load out texture we first need a library that loads the data, for simplicity I will be
|
||||
using SFML, however there are a lot of librarys for loading image data.
|
||||
|
||||
```cpp
|
||||
// Lets save we have a texture called "my_tex.tga", we can load it with:
|
||||
sf::Image image;
|
||||
image.loadFromFile("my_tex.tga");
|
||||
// We have to flip the texture around the y-Axis, because OpenGL's texture
|
||||
// origin is the bottom left corner, not the top left.
|
||||
image.flipVertically();
|
||||
// After loading it we have to create a OpenGL texture.
|
||||
GLuint texture = 0;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
// Specify what happens when the coordinates are out of range.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
// Specify the filtering if the object is very large.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// Load the image data to the texture.
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getSize().x, image.getSize().y,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, image.getPixelsPtr());
|
||||
// Unbind the texture to prevent modifications.
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// Delete the texture at the end of the application.
|
||||
// ...
|
||||
glDeleteTextures(1, &texture);
|
||||
```
|
||||
|
||||
Ofcourse there are more texture formats than only 2D textures,
|
||||
You can find further information on parameters here:
|
||||
[glBindTexture - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTexture.xhtml)<br>
|
||||
[glTexImage2D - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml)<br>
|
||||
[glTexParameter - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml)<br>
|
||||
|
||||
```cpp
|
||||
// With the texture created, we now have to specify the UV,
|
||||
// or in OpenGL terms ST coordinates.
|
||||
std::vector<float> texCoords {
|
||||
// The texture coordinates have to match the triangles/quad
|
||||
// definition.
|
||||
0.0f, 1.0f, // start at top-left
|
||||
0.0f, 0.0f, // go round counter-clockwise
|
||||
1.0f, 0.0f,
|
||||
1.0f, 1.0f // end at top-right
|
||||
};
|
||||
// Now we increase the VBO's size again just like we did for the colors.
|
||||
// ...
|
||||
GLuint vbo[3];
|
||||
glGenBuffers(3, vbo);
|
||||
// ...
|
||||
glDeleteBuffers(3, vbo);
|
||||
// ...
|
||||
// Load the texture coordinates into the new buffer.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords[0]) * texCoords.size(),
|
||||
texCoords.data(), GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
// Because the VAO does not store the texture we have to bind it before drawing.
|
||||
// ...
|
||||
glBindVertexArray(vao);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glDrawElements(GL_TRIANGLES, iboData.size(), GL_UNSINGED_INT, nullptr);
|
||||
// ...
|
||||
```
|
||||
|
||||
Change the shaders to pass the data to the fragment shader.<br>
|
||||
|
||||
**Vertex Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
layout(location = 2) in vec2 texCoords;
|
||||
|
||||
out vec3 fColor;
|
||||
out vec2 fTexCoords;
|
||||
|
||||
void main() {
|
||||
fColor = color;
|
||||
fTexCoords = texCoords;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
**Fragment Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
// sampler2D represents our 2D texture.
|
||||
uniform sampler2D tex;
|
||||
uniform float time;
|
||||
|
||||
in vec3 fColor;
|
||||
in vec2 fTexCoords;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
// texture() loads the current texure data at the specified texture coords,
|
||||
// then we can simply multiply them by our color.
|
||||
outColor = texture(tex, fTexCoords) * vec4(fColor, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
You can find the current code here: [OpenGL - 3](https://pastebin.com/u3bcwM6q)
|
||||
|
||||
## Matrix Transformation
|
||||
|
||||
**Vertex Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
layout(location = 2) in vec2 texCoords;
|
||||
// Create 2 4x4 matricies, 1 for the projection matrix
|
||||
// and 1 for the model matrix.
|
||||
// Because we draw in a static scene, we don't need a view matrix.
|
||||
uniform mat4 projection;
|
||||
uniform mat4 model;
|
||||
|
||||
out vec3 fColor;
|
||||
out vec2 fTexCoords;
|
||||
|
||||
void main() {
|
||||
fColor = color;
|
||||
fTexCoords = texCoords;
|
||||
// Multiplay the position by the model matrix and then by the
|
||||
// projection matrix.
|
||||
// Beware order of multiplication for matricies!
|
||||
gl_Position = projection * model * vec4(position, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
In our source we now need to change the vertex data, create a model- and a projection matrix.
|
||||
|
||||
```cpp
|
||||
// The new vertex data, counter-clockwise declaration.
|
||||
std::vector<float> vertexData {
|
||||
0.0f, 1.0f, 0.0f, // top left
|
||||
0.0f, 0.0f, 0.0f, // bottom left
|
||||
1.0f, 0.0f, 0.0f, // bottom right
|
||||
1.0f, 1.0f, 0.0f // top right
|
||||
};
|
||||
// Request the location of our matricies.
|
||||
GLint projectionLocation = glGetUniformLocation(program, "projection");
|
||||
GLint modelLocation = glGetUniformLocation(program, "model");
|
||||
// Declaring the matricies.
|
||||
// Orthogonal matrix for a 1024x768 window.
|
||||
std::vector<float> projection {
|
||||
0.001953f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -0.002604f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f
|
||||
};
|
||||
// Model matrix translating to x 50, y 50
|
||||
// and scaling to x 200, y 200.
|
||||
std::vector<float> model {
|
||||
200.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 200.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
50.0f, 50.0f, 0.0f, 1.0f
|
||||
};
|
||||
// Now we can send our calculated matricies to the program.
|
||||
glUseProgram(program);
|
||||
glUniformMatrix4fv(projectionLocation, // location
|
||||
1, // count
|
||||
GL_FALSE, // transpose the matrix
|
||||
projection.data()); // data
|
||||
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, model.data());
|
||||
glUseProgram(0);
|
||||
// The glUniform*() calls have to be done, while the program is bound.
|
||||
```
|
||||
|
||||
The application should now display the texture at the defined position and size.<br>
|
||||
You can find the current code here: [OpenGL - 4](https://pastebin.com/9ahpFLkY)
|
||||
|
||||
```cpp
|
||||
// There are many math librarys for OpenGL, which create
|
||||
// matricies and vectors, the most used in C++ is glm (OpenGL Mathematics).
|
||||
// Its a header only library.
|
||||
// The same code using glm would look like:
|
||||
glm::mat4 projection{ glm::ortho(0.0f, 1024.0f, 768.0f, 0.0f) };
|
||||
glUniformMatrix4fv(projectionLocation, 1, GL_FALSE,
|
||||
glm::value_ptr(projection));
|
||||
// Initialise the model matrix to the identity matrix, otherwise every
|
||||
// multiplication would be 0.
|
||||
glm::mat4 model{ 1.0f };
|
||||
model = glm::translate(model, glm::vec3{ 50.0f, 50.0f, 0.0f });
|
||||
model = glm::scale(model, glm::vec3{ 200.0f, 200.0f, 0.0f });
|
||||
glUniformMatrix4fv(modelLocation, 1, GL_FALSE,
|
||||
glm::value_ptr(model));
|
||||
```
|
||||
|
||||
## Geometry Shader
|
||||
|
||||
Gemoetry shaders were introduced in OpenGL 3.2, they can produce vertices
|
||||
that are send to the rasterizer. They can also change the primitive type e.g.
|
||||
they can take a point as an input and output other primitives.
|
||||
Geometry shaders are inbetween the vertex and the fragment shader.
|
||||
|
||||
**Vertex Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
// Create an output interface block passed to the next shadaer stage.
|
||||
// Interface blocks can be used to structure data passed between shaders.
|
||||
out VS_OUT {
|
||||
vec3 color;
|
||||
} vs_out;
|
||||
|
||||
void main() {
|
||||
vs_out.color = color
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
**Geometry Shader**
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
// The geometry shader takes in points.
|
||||
layout(points) in;
|
||||
// It outputs a triangle every 3 vertices emitted.
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
// VS_OUT becomes an input variable in the geometry shader.
|
||||
// Every input to the geometry shader in treated as an array.
|
||||
in VS_OUT {
|
||||
vec3 color;
|
||||
} gs_in[];
|
||||
// Output color for the fragment shader.
|
||||
// You can also simply define color as 'out vec3 color',
|
||||
// If you don't want to use interface blocks.
|
||||
out GS_OUT {
|
||||
vec3 color;
|
||||
} gs_out;
|
||||
|
||||
void main() {
|
||||
// Each emit calls the fragment shader, so we set a color for each vertex.
|
||||
gs_out.color = mix(gs_in[0].color, vec3(1.0, 0.0, 0.0), 0.5);
|
||||
// Move 0.5 units to the left and emit the new vertex.
|
||||
// gl_in[] is the current vertex from the vertex shader, here we only
|
||||
// use 0, because we are receiving points.
|
||||
gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, 0.0);
|
||||
EmitVertex();
|
||||
gs_out.color = mix(gs_in[0].color, vec3(0.0, 1.0, 0.0), 0.5);
|
||||
// Move 0.5 units to the right and emit the new vertex.
|
||||
gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, 0.0);
|
||||
EmitVertex();
|
||||
gs_out.color = mix(gs_in[0].color, vec3(0.0, 0.0, 1.0), 0.5);
|
||||
// Move 0.5 units up and emit the new vertex.
|
||||
gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.75, 0.0, 0.0);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
}
|
||||
```
|
||||
|
||||
**Fragment Shader**
|
||||
|
||||
```glsl
|
||||
in GS_OUT {
|
||||
vec3 color;
|
||||
} fs_in;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fs_in.color, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
If you now store a single point with a single color in a VBO and draw them,
|
||||
you should see a triangle, with your color mixed half way between
|
||||
red, green and blue on each vertex.
|
||||
|
||||
|
||||
## Quotes
|
||||
<sup>[1]</sup>[OpenGL - Wikipedia](https://en.wikipedia.org/wiki/OpenGL)
|
||||
|
||||
## Books
|
||||
|
||||
- OpenGL Superbible - Fifth Edition (covering OpenGL 3.3)
|
||||
- OpenGL Programming Guide - Eighth Edition (covering OpenGL 4.3)
|
@ -247,11 +247,12 @@ concat3(|@array); #=> a, b, c
|
||||
## arguments. If you really need to, you can ask for a mutable container by
|
||||
## using the `is rw` trait:
|
||||
sub mutate( $n is rw ) {
|
||||
$n++;
|
||||
$n++; # postfix ++ operator increments its argument but returns its old value
|
||||
}
|
||||
|
||||
my $m = 42;
|
||||
mutate $m; #=> 43
|
||||
mutate $m; # the value is incremented but the old value is returned
|
||||
#=> 42
|
||||
say $m; #=> 43
|
||||
|
||||
## This works because we are passing the container $m to the `mutate` sub.
|
||||
|
247
protocol-buffer-3.html.markdown
Normal file
247
protocol-buffer-3.html.markdown
Normal file
@ -0,0 +1,247 @@
|
||||
---
|
||||
language: protocol-buffers
|
||||
filename: protocol-buffers.proto
|
||||
contributors:
|
||||
- ["Shankar Shastri", "https://github.com/shankarshastri"]
|
||||
---
|
||||
# Protocol Buffers
|
||||
|
||||
## Why Protocol Buffers
|
||||
|
||||
Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler.
|
||||
You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
|
||||
Protocol Buffers are Schema Of Messages. They are language agnostic.
|
||||
They can be converted to binary and converted back to message formats using the code generated by the protoc compiler for various languages.
|
||||
|
||||
```
|
||||
/*
|
||||
* Language Syntax
|
||||
*/
|
||||
|
||||
/*
|
||||
* Specifying Syntax Of Protocol Buffer Version
|
||||
* Specifying Which Protocol Buffer Version To Use
|
||||
* It can be usually proto3 or proto2
|
||||
*/
|
||||
syntax = "proto3";
|
||||
|
||||
/*
|
||||
* Declaring Message In Protocol Buffer:
|
||||
* As you can see, each field in the message definition has a unique number.
|
||||
* These field numbers are used to identify your fields in the message binary format,
|
||||
* and should not be changed once your message type is in use.
|
||||
* Note that field numbers in the range 1 through 15 take one byte to encode, including the field number and the field's type (you can find out more about this in Protocol Buffer Encoding).
|
||||
* Field numbers in the range 16 through 2047 take two bytes. So you should reserve the numbers 1 through 15 for very frequently occurring message elements.
|
||||
* Remember to leave some room for frequently occurring elements that might be added in the future.
|
||||
* The smallest field number you can specify is 1, and the largest is 2^29 - 1, or 536,870,911.
|
||||
* You also cannot use the numbers 19000 through 19999 (FieldDescriptor::kFirstReservedNumber through FieldDescriptor::kLastReservedNumber),
|
||||
* as they are reserved for the Protocol Buffers implementation - the protocol buffer compiler will complain if you use one of these reserved numbers in your .proto.
|
||||
* Similarly, you cannot use any previously reserved field numbers.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
Syntax For Declaring Message:
|
||||
message ${MessageName} {
|
||||
${Scalar Value Type} ${FieldName1} = ${Tag Number1};
|
||||
.
|
||||
.
|
||||
.
|
||||
${Scalar Value Type} ${FieldNameN} = ${Tag NumberN};
|
||||
}
|
||||
|
||||
Default Values Will be applied any case if the message doesn't contain a existing field defined
|
||||
in the message definition
|
||||
*/
|
||||
|
||||
message MessageTypes {
|
||||
/*
|
||||
* Scalar Value Types
|
||||
*/
|
||||
string stringType = 1; // A string must always contain UTF-8 encoded or 7-bit ASCII text. Default value = ""
|
||||
|
||||
// Number Types, Default Value = 0
|
||||
int32 int32Type = 2; // Uses Variable Length Encoding. Inefficient For Negative Numbers, Instead Use sint32.
|
||||
int64 int64Type = 3; // Uses Variable Length Encoding. Inefficient For Negative Numbers, Instead Use sint64.
|
||||
uint32 uInt32Type = 4; // Uses Variable Length Encoding
|
||||
uint64 uInt64Type = 5; // Uses Variable Length Encoding
|
||||
sint32 sInt32Type = 6; // Uses Variable Length Encoding. They are efficient in encoding for negative numbers.
|
||||
// Use this instead of int32 for negative numbers
|
||||
sint64 sInt64Type = 7; // Uses Variable Length Encoding. They are efficient in encoding for negative numbers.
|
||||
// Use this instead of int64 for negative numbers.
|
||||
|
||||
fixed32 fixed32Type = 8; // Always four bytes. More efficient than uint32 if values are often greater than 2^28.
|
||||
fixed64 fixed64Type = 9; // Always eight bytes. More efficient than uint64 if values are often greater than 2^56
|
||||
|
||||
sfixed32 sfixed32Type = 10; // Always four bytes.
|
||||
sfixed64 sfixed64Type = 11; // Always Eight bytes.
|
||||
|
||||
bool boolType = 12; // Boolean Type. Default Value = false
|
||||
|
||||
bytes bytesType = 13; // May contain any arbitrary sequence of bytes. Default Value = Empty Bytes
|
||||
|
||||
double doubleType = 14;
|
||||
float floatType = 15;
|
||||
|
||||
enum Week {
|
||||
UNDEFINED = 0; // Tag 0 is always used as default in case of enum
|
||||
SUNDAY = 1;
|
||||
MONDAY = 2;
|
||||
TUESDAY = 3;
|
||||
WEDNESDAY = 4;
|
||||
THURSDAY = 5;
|
||||
FRIDAY = 6;
|
||||
SATURDAY = 7;
|
||||
}
|
||||
Week wkDayType = 16;
|
||||
|
||||
/*
|
||||
* Defining Collection Of Scalar Value Type
|
||||
* Syntax: repeated ${ScalarType} ${name} = TagValue
|
||||
*/
|
||||
repeated string listOfString = 17; // List[String]
|
||||
}
|
||||
|
||||
/*
|
||||
* Defining Defined Message Types In Other Message Definition
|
||||
*/
|
||||
message Person {
|
||||
string fname = 1;
|
||||
string sname = 2;
|
||||
}
|
||||
|
||||
message City {
|
||||
Person p = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nested Message Definitions
|
||||
*/
|
||||
|
||||
message NestedMessages {
|
||||
message FirstLevelNestedMessage {
|
||||
string firstString = 1;
|
||||
message SecondLevelNestedMessage {
|
||||
string secondString = 2;
|
||||
}
|
||||
}
|
||||
FirstLevelNestedMessage msg = 1;
|
||||
FirstLevelNestedMessage.SecondLevelNestedMessage msg2 = 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Importing Message From A File
|
||||
*/
|
||||
|
||||
// one.proto
|
||||
// message One {
|
||||
// string oneMsg = 1;
|
||||
// }
|
||||
|
||||
// two.proto
|
||||
// import "myproject/one.proto"
|
||||
// message Two {
|
||||
// string twoMsg = 2;
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
* Advanced Topics
|
||||
*/
|
||||
|
||||
/*
|
||||
* Handling Message Type Changes:
|
||||
* Never Change/Use The TagNumber Of A Message Field Which Was Removed
|
||||
* We should use reserved in case of message definition update.
|
||||
* (https://developers.google.com/protocol-buffers/docs/proto3#updating)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Reserved Fields
|
||||
* It's used in case if we need to add/remove new fields into message.
|
||||
* Using Reserved Backward and Forward Compatibility Of Messages can be achieved
|
||||
*/
|
||||
|
||||
|
||||
message ReservedMessage {
|
||||
reserved 0, 1, 2, 3 to 10; // Set Of Tag Numbers Which Can't be reused.
|
||||
reserved "firstMsg", "secondMsg", "thirdMsg"; // Set Of Labels Which Can't Be reused.
|
||||
}
|
||||
|
||||
/*
|
||||
* Any
|
||||
* The Any message type lets you use messages as embedded types without having their .proto definition.
|
||||
* An Any contains an arbitrary serialized message as bytes,
|
||||
* along with a URL that acts as a globally unique identifier for and resolves to that message's type.
|
||||
* For Any to work we need to import it as shown below.
|
||||
*/
|
||||
/*
|
||||
import "google/protobuf/any.proto";
|
||||
message AnySampleMessage {
|
||||
repeated google.protobuf.Any.details = 1;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* OneOf
|
||||
* There are cases, wherein only one field at-most might be present as part of the message.
|
||||
* Note: OneOf messages can't be repeated.
|
||||
*/
|
||||
|
||||
message OneOfMessage {
|
||||
oneof msg {
|
||||
string fname = 1;
|
||||
string sname = 2;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Maps
|
||||
* Map fields cannot be repeated.
|
||||
* Ordering Of A Map Is Not Guaranteed.
|
||||
*/
|
||||
|
||||
message MessageWithMaps {
|
||||
map<string, string> mapOfMessages = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Packages
|
||||
* Used for preventing name clashes between protocol message types
|
||||
* Syntax:
|
||||
package ${packageName};
|
||||
|
||||
To Access the package;
|
||||
${packageName}.${messageName} = ${tagNumber};
|
||||
*/
|
||||
|
||||
/*
|
||||
* Services
|
||||
* Message Types Defined For Using In RPC system.
|
||||
* When protoc compiler generates for various languages it generates stub methods for the services.
|
||||
*/
|
||||
|
||||
message SearchRequest {
|
||||
string queryString = 1;
|
||||
}
|
||||
|
||||
message SearchResponse {
|
||||
string queryResponse = 1;
|
||||
}
|
||||
service SearchService {
|
||||
rpc Search (SearchRequest) returns (SearchResponse);
|
||||
}
|
||||
```
|
||||
|
||||
## Generating Classes In Various Languages For Protocol Buffers
|
||||
|
||||
```shell
|
||||
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
[Google Protocol Buffers](https://developers.google.com/protocol-buffers/)
|
543
reason.html.markdown
Normal file
543
reason.html.markdown
Normal file
@ -0,0 +1,543 @@
|
||||
---
|
||||
language: reason
|
||||
filename: reason.re
|
||||
contributors:
|
||||
- ["Seth Corker", "https://sethcorker.com"]
|
||||
---
|
||||
|
||||
Reason is a syntax over OCaml that is easier to get started for programmers who are familiar with C-style syntax like JavaScript. BuckleScript is part of the toolchain which compiles Reason to JavaScript so you can write statically typed code for anywhere that JavaScript runs.
|
||||
|
||||
```javascript
|
||||
/* Comments start with slash-star, and end with slash-star */
|
||||
|
||||
/*----------------------------------------------
|
||||
* Variable and function declaration
|
||||
*----------------------------------------------
|
||||
* Variables and functions use the let keyword and end with a semi-colon
|
||||
* `let` bindings are immutable
|
||||
*/
|
||||
|
||||
let x = 5;
|
||||
/* - Notice we didn't add a type, Reason will infer it's an int */
|
||||
|
||||
/* A function like this, take two arguments and add them together */
|
||||
let add = (a, b) => a + b;
|
||||
/* - This doesn't need a type annotation either! */
|
||||
|
||||
/*----------------------------------------------
|
||||
* Type annotation
|
||||
*----------------------------------------------
|
||||
* Types don't need tp be explicitly annotated in most cases but when you need
|
||||
* to, you can add the type after the name
|
||||
*/
|
||||
|
||||
/* A type can be explicitly written like so */
|
||||
let x: int = 5;
|
||||
|
||||
/* The add function from before could be explicitly annotated too */
|
||||
let add2 = (a: int, b: int): int => a + b;
|
||||
|
||||
/* A type can be aliased using the type keyword */
|
||||
type companyId = int;
|
||||
let myId: companyId = 101;
|
||||
|
||||
/* Mutation is not encouraged in Reason but it's there if you need it
|
||||
If you need to mutate a let binding, the value must be wrapped in a `ref()`*/
|
||||
let myMutableNumber = ref(120);
|
||||
|
||||
/* To access the value (and not the ref container), use `^` */
|
||||
let copyOfMyMutableNumber = myMutableNumber^;
|
||||
|
||||
/* To assign a new value, use the `:=` operator */
|
||||
myMutableNumber := 240;
|
||||
|
||||
/*----------------------------------------------
|
||||
* Basic types and operators
|
||||
*----------------------------------------------
|
||||
*/
|
||||
|
||||
/* > String */
|
||||
|
||||
/* Use double quotes for strings */
|
||||
let greeting = "Hello world!";
|
||||
|
||||
/* A string can span multiple lines */
|
||||
let aLongerGreeting = "Look at me,
|
||||
I'm a multi-line string
|
||||
";
|
||||
|
||||
/* A quoted string can be used for string interpolation and special chars
|
||||
Use the `js` annotation for unicode */
|
||||
let world = {js|🌍|js};
|
||||
|
||||
/* The `j` annotation is used for string interpolation */
|
||||
let helloWorld = {j|hello, $world|j};
|
||||
|
||||
/* Concatenate strings with ++ */
|
||||
let name = "John " ++ "Wayne";
|
||||
let emailSubject = "Hi " ++ name ++ ", you're a valued customer";
|
||||
|
||||
/* > Char */
|
||||
|
||||
/* Use a single character for the char type */
|
||||
let lastLetter = 'z';
|
||||
/* - Char doesn't support Unicode or UTF-8 */
|
||||
|
||||
/* > Boolean */
|
||||
|
||||
/* A boolean be either true or false */
|
||||
let isLearning = true;
|
||||
|
||||
true && false; /* - : bool = false; Logical and */
|
||||
true || true; /* - : bool = true; Logical or */
|
||||
!true; /* - : bool = false; Logical not */
|
||||
|
||||
/* Greater than `>`, or greater than or equal to `>=` */
|
||||
'a' > 'b'; /* - bool : false */
|
||||
|
||||
/* Less than `<`, or less than or equal to `<=` */
|
||||
1 < 5; /* - : bool = true */
|
||||
|
||||
/* Structural equal */
|
||||
"hello" == "hello"; /* - : bool = true */
|
||||
|
||||
/* Referential equal */
|
||||
"hello" === "hello"; /* - : bool = false */
|
||||
/* - This is false because they are two different "hello" string literals */
|
||||
|
||||
/* Structural unequal */
|
||||
lastLetter != 'a'; /* -: bool = true */
|
||||
|
||||
/* Referential unequal */
|
||||
lastLetter !== lastLetter; /* - : bool = false */
|
||||
|
||||
/* > Integer */
|
||||
/* Perform math operations on integers */
|
||||
|
||||
1 + 1; /* - : int = 2 */
|
||||
25 - 11; /* - : int = 11 */
|
||||
5 * 2 * 3; /* - : int = 30 */
|
||||
8 / 2; /* - : int = 4 */
|
||||
|
||||
/* > Float */
|
||||
/* Operators on floats have a dot after them */
|
||||
|
||||
1.1 +. 1.5; /* - : float = 2.6 */
|
||||
18.0 -. 24.5; /* - : float = -6.5 */
|
||||
2.5 *. 2.0; /* - : float = 5. */
|
||||
16.0 /. 4.0; /* - : float = 4. */
|
||||
|
||||
/* > Tuple
|
||||
* Tuples have the following attributes
|
||||
- immutable
|
||||
- ordered
|
||||
- fix-sized at creation time
|
||||
- heterogeneous (can contain different types of values)
|
||||
A tuple is 2 or more values */
|
||||
|
||||
let teamMember = ("John", 25);
|
||||
|
||||
/* Type annotation matches the values */
|
||||
let position2d: (float, float) = (9.0, 12.0);
|
||||
|
||||
/* Pattern matching is a great tool to retrieve just the values you care about
|
||||
If we only want the y value, let's use `_` to ignore the value */
|
||||
let (_, y) = position2d;
|
||||
y +. 1.0; /* - : float = 13. */
|
||||
|
||||
/* > Record */
|
||||
|
||||
/* A record has to have an explicit type */
|
||||
type trainJourney = {
|
||||
destination: string,
|
||||
capacity: int,
|
||||
averageSpeed: float,
|
||||
};
|
||||
|
||||
/* Once the type is declared, Reason can infer it whenever it comes up */
|
||||
let firstTrip = {destination: "London", capacity: 45, averageSpeed: 120.0};
|
||||
|
||||
/* Access a property using dot notation */
|
||||
let maxPassengers = firstTrip.capacity;
|
||||
|
||||
/* If you define the record type in a different file, you have to reference the
|
||||
filename, if trainJourney was in a file called Trips.re */
|
||||
let secondTrip: Trips.firstTrip = {
|
||||
destination: "Paris",
|
||||
capacity: 50,
|
||||
averageSpeed: 150.0,
|
||||
};
|
||||
|
||||
/* Records are immutable by default */
|
||||
/* But the contents of a record can be copied using the spread operator */
|
||||
let newTrip = {...secondTrip, averageSpeed: 120.0};
|
||||
|
||||
/* A record property can be mutated explicitly with the `mutable` keyword */
|
||||
type breakfastCereal = {
|
||||
name: string,
|
||||
mutable amount: int,
|
||||
};
|
||||
|
||||
let tastyMuesli = {name: "Tasty Muesli TM", amount: 500};
|
||||
|
||||
tastyMuesli.amount = 200;
|
||||
/* - tastyMuesli now has an amount of 200 */
|
||||
|
||||
/* Punning is used to avoid redundant typing */
|
||||
let name = "Just As Good Muesli";
|
||||
let justAsGoodMuesli = {name, amount: 500};
|
||||
/* - justAsGoodMuesli.name is now "Just As Good Muesli", it's equivalent
|
||||
to { name: name, amount: 500 } */
|
||||
|
||||
/* > Variant
|
||||
Mutually exclusive states can be expressed with variants */
|
||||
|
||||
type authType =
|
||||
| GitHub
|
||||
| Facebook
|
||||
| Google
|
||||
| Password;
|
||||
/* - The constructors must be capitalized like so */
|
||||
/* - Like records, variants should be named if declared in a different file */
|
||||
|
||||
let userPreferredAuth = GitHub;
|
||||
|
||||
/* Variants work great with a switch statement */
|
||||
let loginMessage =
|
||||
switch (userPreferredAuth) {
|
||||
| GitHub => "Login with GitHub credentials."
|
||||
| Facebook => "Login with your Facebook account."
|
||||
| Google => "Login with your Google account"
|
||||
| Password => "Login with email and password."
|
||||
};
|
||||
|
||||
/* > Option
|
||||
An option can be None or Some('a) where 'a is the type */
|
||||
|
||||
let userId = Some(23);
|
||||
|
||||
/* A switch handles the two cases */
|
||||
let alertMessage =
|
||||
switch (userId) {
|
||||
| Some(id) => "Welcome, your ID is" ++ string_of_int(id)
|
||||
| None => "You don't have an account!"
|
||||
};
|
||||
/* - Missing a case, `None` or `Some`, would cause an error */
|
||||
|
||||
/* > List
|
||||
* Lists have the following attributes
|
||||
- immutable
|
||||
- ordered
|
||||
- fast at prepending items
|
||||
- fast at splitting
|
||||
|
||||
* Lists in Reason are linked lists
|
||||
*/
|
||||
|
||||
/* A list is declared with square brackets */
|
||||
let userIds = [1, 4, 8];
|
||||
|
||||
/* The type can be explicitly set with list('a) where 'a is the type */
|
||||
type idList = list(int);
|
||||
type attendanceList = list(string);
|
||||
|
||||
/* Lists are immutable */
|
||||
/* But the contents of a list can be copied using the spread operator */
|
||||
let newUserIds = [101, 102, ...userIds];
|
||||
|
||||
/* > Array
|
||||
* Arrays have the following attributes
|
||||
- mutable
|
||||
- fast at random access & updates */
|
||||
|
||||
/* An array is declared with `[|` and ends with `|]` */
|
||||
let languages = [|"Reason", "JavaScript", "OCaml"|];
|
||||
|
||||
/*----------------------------------------------
|
||||
* Function
|
||||
*----------------------------------------------
|
||||
*/
|
||||
|
||||
/* Reason functions use the arrow syntax, the expression is returned */
|
||||
let signUpToNewsletter = email => "Thanks for signing up " ++ email;
|
||||
|
||||
/* Call a function like this */
|
||||
signUpToNewsletter("hello@reason.org");
|
||||
|
||||
/* For longer functions, use a block */
|
||||
let getEmailPrefs = email => {
|
||||
let message = "Update settings for " ++ email;
|
||||
let prefs = ["Weekly News", "Daily Notifications"];
|
||||
|
||||
(message, prefs);
|
||||
};
|
||||
/* - the final tuple is implicitly returned */
|
||||
|
||||
/* > Labeled Arguments */
|
||||
|
||||
/* Arguments can be labeled with the ~ symbol */
|
||||
let moveTo = (~x, ~y) => {/* Move to x,y */};
|
||||
|
||||
moveTo(~x=7.0, ~y=3.5);
|
||||
|
||||
/* Labeled arguments can also have a name used within the function */
|
||||
let getMessage = (~message as msg) => "==" ++ msg ++ "==";
|
||||
|
||||
getMessage(~message="You have a message!");
|
||||
/* - The caller specifies ~message but internally the function can make use */
|
||||
|
||||
/* The following function also has explicit types declared */
|
||||
let showDialog = (~message: string): unit => {
|
||||
() /* Show the dialog */;
|
||||
};
|
||||
/* - The return type is `unit`, this is a special type that is equivalent to
|
||||
specifying that this function doesn't return a value
|
||||
the `unit` type can also be represented as `()` */
|
||||
|
||||
/* > Currying
|
||||
Functions can be curried and are partially called, allowing for easy reuse */
|
||||
|
||||
let div = (denom, numr) => numr / denom;
|
||||
let divBySix = div(6);
|
||||
let divByTwo = div(2);
|
||||
|
||||
div(3, 24); /* - : int = 8 */
|
||||
divBySix(128); /* - : int = 21 */
|
||||
divByTwo(10); /* - : int = 5 */
|
||||
|
||||
/* > Optional Labeled Arguments */
|
||||
|
||||
/* Use `=?` syntax for optional labeled arguments */
|
||||
let greetPerson = (~name, ~greeting=?, ()) => {
|
||||
switch (greeting) {
|
||||
| Some(greet) => greet ++ " " ++ name
|
||||
| None => "Hi " ++ name
|
||||
};
|
||||
};
|
||||
/* - The third argument, `unit` or `()` is required because if we omitted it,
|
||||
the function would be curried so greetPerson(~name="Kate") would create
|
||||
a partial function, to fix this we add `unit` when we declare and call it */
|
||||
|
||||
/* Call greetPerson without the optional labeled argument */
|
||||
greetPerson(~name="Kate", ());
|
||||
|
||||
/* Call greetPerson with all arguments */
|
||||
greetPerson(~name="Marco", ~greeting="How are you today,");
|
||||
|
||||
/* > Pipe */
|
||||
/* Functions can be called with the pipeline operator */
|
||||
|
||||
/* Use `->` to pass in the first argument (pipe-first) */
|
||||
3->div(24); /* - : int = 8 */
|
||||
/* - This is equivalent to div(3, 24); */
|
||||
|
||||
36->divBySix; /* - : int = 6 */
|
||||
/* - This is equivalent to divBySix(36); */
|
||||
|
||||
/* Use `|>` to pass in the last argument (pipe-last) */
|
||||
24 |> div(3); /* - : int = 8 */
|
||||
/* - This is equivalent to div(3, 24); */
|
||||
|
||||
36 |> divBySix; /* - : int = 6 */
|
||||
/* - This is equivalent to divBySix(36); */
|
||||
|
||||
/* Pipes make it easier to chain code together */
|
||||
let addOne = a => a + 1;
|
||||
let divByTwo = a => a / 2;
|
||||
let multByThree = a => a * 3;
|
||||
|
||||
let pipedValue = 3->addOne->divByTwo->multByThree; /* - : int = 6 */
|
||||
|
||||
/*----------------------------------------------
|
||||
* Control Flow & Pattern Matching
|
||||
*----------------------------------------------
|
||||
*/
|
||||
|
||||
/* > If-else */
|
||||
/* In Reason, `If` is an expression when evaluate will return the result */
|
||||
|
||||
/* greeting will be "Good morning!" */
|
||||
let greeting = if (true) {"Good morning!"} else {"Hello!"};
|
||||
|
||||
/* Without an else branch the expression will return `unit` or `()` */
|
||||
if (false) {
|
||||
showDialog(~message="Are you sure you want to leave?");
|
||||
};
|
||||
/* - Because the result will be of type `unit`, both return types should be of
|
||||
the same type if you want to assign the result. */
|
||||
|
||||
/* > Destructuring */
|
||||
/* Extract properties from data structures easily */
|
||||
|
||||
let aTuple = ("Teacher", 101);
|
||||
|
||||
/* We can extract the values of a tuple */
|
||||
let (name, classNum) = aTuple;
|
||||
|
||||
/* The properties of a record can be extracted too */
|
||||
type person = {
|
||||
firstName: string,
|
||||
age: int,
|
||||
};
|
||||
let bjorn = {firstName: "Bjorn", age: 28};
|
||||
|
||||
/* The variable names have to match with the record property names */
|
||||
let {firstName, age} = bjorn;
|
||||
|
||||
/* But we can rename them like so */
|
||||
let {firstName: bName, age: bAge} = bjorn;
|
||||
|
||||
let {firstName: cName, age: _} = bjorn;
|
||||
|
||||
/* > Switch
|
||||
Pattern matching with switches is an important tool in Reason
|
||||
It can be used in combination with destructuring for an expressive and
|
||||
concise tool */
|
||||
|
||||
/* Lets take a simple list */
|
||||
let firstNames = ["James", "Jean", "Geoff"];
|
||||
|
||||
/* We can pattern match on the names for each case we want to handle */
|
||||
switch (firstNames) {
|
||||
| [] => "No names"
|
||||
| [first] => "Only " ++ first
|
||||
| [first, second] => "A couple of names " ++ first ++ "," ++ second
|
||||
| [first, second, third] =>
|
||||
"Three names, " ++ first ++ ", " ++ second ++ ", " ++ third
|
||||
| _ => "Lots of names"
|
||||
};
|
||||
/* - The `_` is a catch all at the end, it signifies that we don't care what
|
||||
the value is so it will match every other case */
|
||||
|
||||
/* > When clause */
|
||||
|
||||
let isJohn = a => a == "John";
|
||||
let maybeName = Some("John");
|
||||
|
||||
/* When can add more complex logic to a simple switch */
|
||||
let aGreeting =
|
||||
switch (maybeName) {
|
||||
| Some(name) when isJohn(name) => "Hi John! How's it going?"
|
||||
| Some(name) => "Hi " ++ name ++ ", welcome."
|
||||
| None => "No one to greet."
|
||||
};
|
||||
|
||||
/* > Exception */
|
||||
|
||||
/* Define a custom exception */
|
||||
exception Under_Age;
|
||||
|
||||
/* Raise an exception within a function */
|
||||
let driveToTown = (driver: person) =>
|
||||
if (driver.age >= 15) {
|
||||
"We're in town";
|
||||
} else {
|
||||
raise(Under_Age);
|
||||
};
|
||||
|
||||
let evan = {firstName: "Evan", age: 14};
|
||||
|
||||
/* Pattern match on the exception Under_Age */
|
||||
switch (driveToTown(evan)) {
|
||||
| status => print_endline(status)
|
||||
| exception Under_Age =>
|
||||
print_endline(evan.firstName ++ " is too young to drive!")
|
||||
};
|
||||
|
||||
/* Alternatively, a try block can be used */
|
||||
let messageToEvan =
|
||||
try (driveToTown(evan)) {
|
||||
| Under_Age => evan.firstName ++ " is too young to drive!"
|
||||
};
|
||||
/* - With Reason exceptions can be avoided with optionals and are seldom used */
|
||||
|
||||
/*----------------------------------------------
|
||||
* Object
|
||||
*----------------------------------------------
|
||||
* Objects are similar to Record types but aren't as rigid
|
||||
* An object resembles a class
|
||||
*/
|
||||
|
||||
/* An object may be typed like a record but contains a dot */
|
||||
type surfaceComputer = {
|
||||
.
|
||||
color: string,
|
||||
capacity: int,
|
||||
};
|
||||
/* - A single dot signifies a closed object, an object that uses this type
|
||||
must have the exact shape */
|
||||
|
||||
let surfaceBook: surfaceComputer = {pub color = "blue"; pub capacity = 512};
|
||||
|
||||
/* But an object doesn't require a type */
|
||||
let house = {
|
||||
/* A private property */
|
||||
val temp = ref(18.0);
|
||||
/* Public properties */
|
||||
pub temperature = temp;
|
||||
/* A private method only accessible from within house */
|
||||
pri setThermostat = v => temp := v;
|
||||
/* A public method that calls the private setThermostat method */
|
||||
pub arriveHome = () => this#setThermostat(22.0)
|
||||
};
|
||||
|
||||
house#temperature; /* - : float = 18. */
|
||||
house#arriveHome();
|
||||
house#temperature; /* - : float = 22. */
|
||||
|
||||
/*----------------------------------------------
|
||||
* Module
|
||||
*----------------------------------------------
|
||||
* Modules are used to organize your code and provide namespacing,
|
||||
* Each file is a module by default
|
||||
*/
|
||||
|
||||
/* Create a module */
|
||||
module Staff = {
|
||||
type role =
|
||||
| Delivery
|
||||
| Sales
|
||||
| Other;
|
||||
type member = {
|
||||
name: string,
|
||||
role,
|
||||
};
|
||||
|
||||
let getRoleDirectionMessage = staff =>
|
||||
switch (staff.role) {
|
||||
| Delivery => "Deliver it like you mean it!"
|
||||
| Sales => "Sell it like only you can!"
|
||||
| Other => "You're an important part of the team!"
|
||||
};
|
||||
};
|
||||
|
||||
/* A module can be accessed with dot notation */
|
||||
let newEmployee: Staff.member = {name: "Laura", role: Staff.Delivery};
|
||||
|
||||
/* Using the module name can be tiresome so the module's contents can be opened
|
||||
into the current scope with `open` */
|
||||
open Staff;
|
||||
|
||||
let otherNewEmployee: member = {name: "Fred", role: Other};
|
||||
|
||||
/* A module can be extended using the `include` keyword, include copies
|
||||
the contents of the module into the scope of the new module */
|
||||
module SpecializedStaff = {
|
||||
include Staff;
|
||||
|
||||
/* `member` is included so there's no need to reference it explicitly */
|
||||
let ceo: member = {name: "Reggie", role: Other};
|
||||
|
||||
let getMeetingTime = staff =>
|
||||
switch (staff) {
|
||||
| Other => 11_15 /* - : int = 1115; Underscores are for formatting only */
|
||||
| _ => 9_30
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
- [Official Reason Docs](https://reasonml.github.io/docs/en/what-and-why)
|
||||
- [Official BuckleScript Docs](https://bucklescript.github.io/docs/en/what-why)
|
||||
- [Try Reason](https://reasonml.github.io/en/try)
|
||||
- [Get Started with Reason by Nik Graf](https://egghead.io/courses/get-started-with-reason)
|
203
ru-ru/linker-ru.html.markdown
Normal file
203
ru-ru/linker-ru.html.markdown
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
category: tool
|
||||
tool: linker
|
||||
contributors:
|
||||
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
|
||||
translators:
|
||||
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
|
||||
lang: ru-ru
|
||||
---
|
||||
|
||||
# Основные понятия и определения
|
||||
**Счетчик позиций** - у компоновщика есть специальная переменная
|
||||
"." (точка) всегда содержит текущую позицию вывода.
|
||||
|
||||
# Функции
|
||||
**ADDR(section)** - возвращает абсолютный адрес указанной секции. Однако
|
||||
данная секция должна быть определенна до использования функции ADDR.
|
||||
|
||||
**ALIGN(exp)** - возвращает значение счетчика позиций, выравненное на границу
|
||||
следующего за exp выражения.
|
||||
|
||||
**SIZEOF(section)** - возвращает размер секции в байтах.
|
||||
|
||||
**FILL(param)** - определяет образец заполнения для текущей секции. Все
|
||||
остальные неуказанные регионы внутри секции заполняются значением указанными
|
||||
в аргументе функции.
|
||||
|
||||
**KEEP(param)** - используется чтобы помечать param как неустранимый.
|
||||
|
||||
**ENTRY(func)** - определяет функцию, которая будет являться точкой входа
|
||||
в программу.
|
||||
|
||||
```bash
|
||||
# Определяем точку входа в программу
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
# Определяем перемнную которая содержит адрес вершины стека
|
||||
_estack = 0x20020000;
|
||||
# Определяем перемнную которая содержит значение размера кучи
|
||||
_Min_Heap_Size = 0x200;
|
||||
# Определяем перемнную которая содержит значение размера стека
|
||||
_Min_Stack_Size = 0x400;
|
||||
|
||||
# Описание карты памяти доступной для данного процессора
|
||||
# MEMORY
|
||||
# {
|
||||
# ИМЯ_ОБЛАСТИ_ПАМЯТИ (права доступа) : ORIGIN = АДРЕС_НАЧАЛА, LENGTH = РАЗМЕР
|
||||
# }
|
||||
# В нашем примере контроллер содержит три области памяти:
|
||||
# RAM - начинается с адреса 0x20000000 и занимает 128 Кбайт;
|
||||
# CCMRAM - начинается с адреса 0x10000000и занимает 64 Кбайт;
|
||||
# FLASH - начинается с адреса 0x8000000 занимает 1024 Кбайт;
|
||||
# Причем RAM память доступка для чтения, записи и исполнения.
|
||||
# CCMRAM память доступна только на чтение и запись.
|
||||
# FLASH память доступна на чтение и исполнение.
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
|
||||
}
|
||||
|
||||
# Описываем выходные секции
|
||||
SECTIONS
|
||||
{
|
||||
# Первая секция содержит таблицу векторов прерываний
|
||||
.isr_vector :
|
||||
{
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Существует опция --gc-sections, которая позволяет собирать мусор из неиспользуемых
|
||||
# входных разделов. И если есть разделы, которые сборщик муссора не должен трогать,
|
||||
# то их необходимо указать в качестве аргумента функции KEEP() (аналог ключевого слова
|
||||
# volatile).
|
||||
# Запись (*(.isr_vector)) означает разделы .isr_vector во всех объектных файлах. Т.к.
|
||||
# обращение к разделу в общем виде выглядит так: (ИМЯ_ФАЙЛА(ИМЯ_РАЗДЕЛА))
|
||||
KEEP(*(.isr_vector))
|
||||
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Выражение ">ОБЛАСТЬ_ПАМЯТИ" указывает в какую именно область памяти будет помещенна
|
||||
# данная секция. В нашем слущае секция .isr_vector будет размещена во FLASH памяти.
|
||||
} >FLASH
|
||||
|
||||
# ИТОГО: Секция .isr_vector, которая содержит таблицу векторов прерываний выравнивается
|
||||
# по границе 4-х байт, помечается как недоступная для сборщика мусора и размещается в начале
|
||||
# FLASH памяти микроконтроллера.
|
||||
|
||||
# Вторая секция содержит код программы.
|
||||
.text :
|
||||
{
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Указываем, что в данной секции будут хранится области .text всех
|
||||
# объектных файлов
|
||||
*(.text)
|
||||
*(.text*)
|
||||
|
||||
# Защищаем от сборщика мусора секции .init и .fini
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Определяется переменная _etext, которая хранит в себе адрес конца секции .text и которая
|
||||
# может быть доступна в исходном тексте программы через объявление
|
||||
# volaile unsigned int extern _etext;
|
||||
_etext = .;
|
||||
} >FLASH
|
||||
|
||||
# ИТОГО: Секция .text, которая содержит код программы выравнивается по границе 4-х байт,
|
||||
# включает в себя: все секции с кодом программы во всех объектных файлах и защищенные
|
||||
от сборщика муссора секции .init и .fini во всех объектных файлах, распологается во FLASH
|
||||
памяти микроконтроллера сразу за таблицей векторов.
|
||||
Секции text, .init и .fini. располагаются в памяти в той последовательности в которой они
|
||||
объявлены в скрипте.
|
||||
|
||||
# Третья секция содержит константные данные.
|
||||
.rodata :
|
||||
{
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Указываем, что в данной секции будут хранится области .rodataвсех
|
||||
# объектных файлов
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
# Сохраняем в переменной _sidata абсолютный адрес секции .data
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
# Четвертая секция содержит инициализированные переменные.
|
||||
.data :
|
||||
{
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Сохраняем в переменной _sdata адрес текущей позиции (начала секции)
|
||||
_sdata = .;
|
||||
|
||||
# Указываем, что в данной секции будут хранится области .data всех
|
||||
# объектных файлов
|
||||
*(.data)
|
||||
*(.data*)
|
||||
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Сохраняем в переменной _sdata адрес текущей позиции (конец секции)
|
||||
_edata = .;
|
||||
|
||||
# Функция AT указывает на то, что данный сектор хранится в одной области памяти
|
||||
# (в нашем случае FLASH), а исполняться будет из другой обасти памяти (в нашем случае RAM).
|
||||
# Есть два типа адрессов:
|
||||
# * VMA (Virtual memory address) - это run-time адрес по которому уомпилятор ожидает
|
||||
# видеть данные.
|
||||
# * LMA (Load memory address) - это адрес по которому линкер хранит данные.
|
||||
|
||||
#Startup должен код скопировать секцию .data из адрессов LMA в адресса VMA.
|
||||
|
||||
} >RAM AT> FLASH
|
||||
|
||||
# Пятая секция содержит инициализированные нулем переменные.
|
||||
.bss :
|
||||
{
|
||||
# Сохраняем в переменной _sbss и __bss_start__ адрес текущей позиции (начала секции)
|
||||
_sbss = .;
|
||||
__bss_start__ = _sbss;
|
||||
|
||||
# Указываем, что в данной секции будут хранится области .bss всех
|
||||
# объектных файлов
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
|
||||
# Выравниваем текущую позицию на границу 4-х байт.
|
||||
. = ALIGN(4);
|
||||
|
||||
# Сохраняем в переменной _ebss и __bss_end__ адрес текущей позиции (начала секции)
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
# Шестая секция содержит кучу и стек. Размещается в самом конце RAM.
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(4);
|
||||
} >RAM
|
||||
}
|
||||
```
|
||||
|
@ -345,7 +345,7 @@ s(0) // Boolean = false
|
||||
s(1) // Boolean = true
|
||||
|
||||
/* Look up the documentation of map here -
|
||||
* http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
|
||||
* https://www.scala-lang.org/api/current/scala/collection/immutable/Map.html
|
||||
* and make sure you can read it
|
||||
*/
|
||||
|
||||
|
1017
swift.html.markdown
1017
swift.html.markdown
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ filename: learnpascal.pas
|
||||
contributors:
|
||||
- ["Ganesha Danu", "http://github.com/blinfoldking"]
|
||||
- ["Keith Miyake", "https://github.com/kaymmm"]
|
||||
translator:
|
||||
translators:
|
||||
- ["Worajedt Sitthidumrong", "https://bitbucket.org/wrj"]
|
||||
lang: th-th
|
||||
---
|
||||
|
@ -224,6 +224,19 @@ moreNumbers.push(5); // Error, no push method (because it mutates array)
|
||||
moreNumbers.length = 3; // Error, length is read-only
|
||||
numbers = moreNumbers; // Error, mutating methods are missing
|
||||
|
||||
// Tagged Union Types for modelling state that can be in one of many shapes
|
||||
type State =
|
||||
| { type: "loading" }
|
||||
| { type: "success", value: number }
|
||||
| { type: "error", message: string };
|
||||
|
||||
declare const state: State;
|
||||
if (state.type === "success") {
|
||||
console.log(state.value);
|
||||
} else if (state.type === "error") {
|
||||
console.error(state.message);
|
||||
}
|
||||
|
||||
// Iterators and Generators
|
||||
|
||||
// for..of statement
|
||||
@ -235,13 +248,13 @@ for (const val of arrayOfAnyType) {
|
||||
|
||||
let list = [4, 5, 6];
|
||||
for (const i of list) {
|
||||
console.log(i); // "4", "5", "6"
|
||||
console.log(i); // 4, 5, 6
|
||||
}
|
||||
|
||||
// for..in statement
|
||||
// iterate over the list of keys on the object being iterated
|
||||
for (const i in list) {
|
||||
console.log(i); // "0", "1", "2",
|
||||
console.log(i); // 0, 1, 2
|
||||
}
|
||||
|
||||
|
||||
|
227
wasm.html.markdown
Normal file
227
wasm.html.markdown
Normal file
@ -0,0 +1,227 @@
|
||||
---
|
||||
language: WebAssembly
|
||||
filename: learn-wasm.wast
|
||||
contributors:
|
||||
- ["Dean Shaff", "http://dean-shaff.github.io"]
|
||||
---
|
||||
|
||||
```
|
||||
;; learn-wasm.wast
|
||||
|
||||
(module
|
||||
;; In WebAssembly, everything is included in a module. Moreover, everything
|
||||
;; can be expressed as an s-expression. Alternatively, there is the
|
||||
;; "stack machine" syntax, but that is not compatible with Binaryen
|
||||
;; intermediate representation (IR) syntax.
|
||||
|
||||
;; The Binaryen IR format is *mostly* compatible with WebAssembly text format.
|
||||
;; There are some small differences:
|
||||
;; local_set -> local.set
|
||||
;; local_get -> local.get
|
||||
|
||||
;; We have to enclose code in functions
|
||||
|
||||
;; Data Types
|
||||
(func $data_types
|
||||
;; WebAssembly has only four types:
|
||||
;; i32 - 32 bit integer
|
||||
;; i64 - 64 bit integer (not supported in JavaScript)
|
||||
;; f32 - 32 bit floating point
|
||||
;; f64 - 64 bit floating point
|
||||
|
||||
;; We can declare local variables with the "local" keyword
|
||||
;; We have to declare all variables before we start doing anything
|
||||
;; inside the function
|
||||
|
||||
(local $int_32 i32)
|
||||
(local $int_64 i64)
|
||||
(local $float_32 f32)
|
||||
(local $float_64 f64)
|
||||
|
||||
;; These values remain uninitialized.
|
||||
;; To set them to a value, we can use <type>.const:
|
||||
|
||||
(local.set $int_32 (i32.const 16))
|
||||
(local.set $int_32 (i64.const 128))
|
||||
(local.set $float_32 (f32.const 3.14))
|
||||
(local.set $float_64 (f64.const 1.28))
|
||||
)
|
||||
|
||||
;; Basic operations
|
||||
(func $basic_operations
|
||||
|
||||
;; In WebAssembly, everything is an s-expression, including
|
||||
;; doing math, or getting the value of some variable
|
||||
|
||||
(local $add_result i32)
|
||||
(local $mult_result f64)
|
||||
|
||||
(local.set $add_result (i32.add (i32.const 2) (i32.const 4)))
|
||||
;; the value of add_result is now 6!
|
||||
|
||||
;; We have to use the right data type for each operation:
|
||||
;; (local.set $mult_result (f32.mul (f32.const 2.0) (f32.const 4.0))) ;; WRONG! mult_result is f64!
|
||||
(local.set $mult_result (f64.mul (f64.const 2.0) (f64.const 4.0))) ;; WRONG! mult_result is f64!
|
||||
|
||||
;; WebAssembly has some builtin operations, like basic math and bitshifting.
|
||||
;; Notably, it does not have built in trigonometric functions.
|
||||
;; In order to get access to these functions, we have to either
|
||||
;; - implement them ourselves (not recommended)
|
||||
;; - import them from elsewhere (later on)
|
||||
)
|
||||
|
||||
;; Functions
|
||||
;; We specify arguments with the `param` keyword, and specify return values
|
||||
;; with the `result` keyword
|
||||
;; The current value on the stack is the return value of a function
|
||||
|
||||
;; We can call other functions we've defined with the `call` keyword
|
||||
|
||||
(func $get_16 (result i32)
|
||||
(i32.const 16)
|
||||
)
|
||||
|
||||
(func $add (param $param0 i32) (param $param1 i32) (result i32)
|
||||
(i32.add
|
||||
(local.get $param0)
|
||||
(local.get $param1)
|
||||
)
|
||||
)
|
||||
|
||||
(func $double_16 (result i32)
|
||||
(i32.mul
|
||||
(i32.const 2)
|
||||
(call $get_16))
|
||||
)
|
||||
|
||||
;; Up until now, we haven't be able to print anything out, nor do we have
|
||||
;; access to higher level math functions (pow, exp, or trig functions).
|
||||
;; Moreover, we haven't been able to use any of the WASM functions in Javascript!
|
||||
;; The way we get those functions into WebAssembly
|
||||
;; looks different whether we're in a Node.js or browser environment.
|
||||
|
||||
;; If we're in Node.js we have to do two steps. First we have to convert the
|
||||
;; WASM text representation into actual webassembly. If we're using Binyaren,
|
||||
;; we can do that with a command like the following:
|
||||
|
||||
;; wasm-as learn-wasm.wast -o learn-wasm.wasm
|
||||
|
||||
;; We can apply Binaryen optimizations to that file with a command like the
|
||||
;; following:
|
||||
|
||||
;; wasm-opt learn-wasm.wasm -o learn-wasm.opt.wasm -O3 --rse
|
||||
|
||||
;; With our compiled WebAssembly, we can now load it into Node.js:
|
||||
;; const fs = require('fs')
|
||||
;; const instantiate = async function (inFilePath, _importObject) {
|
||||
;; var importObject = {
|
||||
;; console: {
|
||||
;; log: (x) => console.log(x),
|
||||
;; },
|
||||
;; math: {
|
||||
;; cos: (x) => Math.cos(x),
|
||||
;; }
|
||||
;; }
|
||||
;; importObject = Object.assign(importObject, _importObject)
|
||||
;;
|
||||
;; var buffer = fs.readFileSync(inFilePath)
|
||||
;; var module = await WebAssembly.compile(buffer)
|
||||
;; var instance = await WebAssembly.instantiate(module, importObject)
|
||||
;; return instance.exports
|
||||
;; }
|
||||
;;
|
||||
;; const main = function () {
|
||||
;; var wasmExports = await instantiate('learn-wasm.wasm')
|
||||
;; wasmExports.print_args(1, 0)
|
||||
;; }
|
||||
|
||||
;; The following snippet gets the functions from the importObject we defined
|
||||
;; in the JavaScript instantiate async function, and then exports a function
|
||||
;; "print_args" that we can call from Node.js
|
||||
|
||||
(import "console" "log" (func $print_i32 (param i32)))
|
||||
(import "math" "cos" (func $cos (param f64) (result f64)))
|
||||
|
||||
(func $print_args (param $arg0 i32) (param $arg1 i32)
|
||||
(call $print_i32 (local.get $arg0))
|
||||
(call $print_i32 (local.get $arg1))
|
||||
)
|
||||
(export "print_args" (func $print_args))
|
||||
|
||||
;; Loading in data from WebAssembly memory.
|
||||
;; Say that we want to apply the cosine function to a Javascript array.
|
||||
;; We need to be able to access the allocated array, and iterate through it.
|
||||
;; This example will modify the input array inplace.
|
||||
;; f64.load and f64.store expect the location of a number in memory *in bytes*.
|
||||
;; If we want to access the 3rd element of an array, we have to pass something
|
||||
;; like (i32.mul (i32.const 8) (i32.const 2)) to the f64.store function.
|
||||
|
||||
;; In JavaScript, we would call `apply_cos64` as follows
|
||||
;; (using the instantiate function from earlier):
|
||||
;;
|
||||
;; const main = function () {
|
||||
;; var wasm = await instantiate('learn-wasm.wasm')
|
||||
;; var n = 100
|
||||
;; const memory = new Float64Array(wasm.memory.buffer, 0, n)
|
||||
;; for (var i=0; i<n; i++) {
|
||||
;; memory[i] = i;
|
||||
;; }
|
||||
;; wasm.apply_cos64(n)
|
||||
;; }
|
||||
;;
|
||||
;; This function will not work if we allocate a Float32Array on the JavaScript
|
||||
;; side.
|
||||
|
||||
(memory (export "memory") 100)
|
||||
|
||||
(func $apply_cos64 (param $array_length i32)
|
||||
;; declare the loop counter
|
||||
(local $idx i32)
|
||||
;; declare the counter that will allow us to access memory
|
||||
(local $idx_bytes i32)
|
||||
;; constant expressing the number of bytes in a f64 number.
|
||||
(local $bytes_per_double i32)
|
||||
|
||||
;; declare a variable for storing the value loaded from memory
|
||||
(local $temp_f64 f64)
|
||||
|
||||
(local.set $idx (i32.const 0))
|
||||
(local.set $idx_bytes (i32.const 0)) ;; not entirely necessary
|
||||
(local.set $bytes_per_double (i32.const 8))
|
||||
|
||||
(block
|
||||
(loop
|
||||
;; this sets idx_bytes to bytes offset of the value we're interested in.
|
||||
(local.set $idx_bytes (i32.mul (local.get $idx) (local.get $bytes_per_double)))
|
||||
|
||||
;; get the value of the array from memory:
|
||||
(local.set $temp_f64 (f64.load (local.get $idx_bytes)))
|
||||
|
||||
;; now apply the cosine function:
|
||||
(local.set $temp_64 (call $cos (local.get $temp_64)))
|
||||
|
||||
;; now store the result at the same location in memory:
|
||||
(f64.store
|
||||
(local.get $idx_bytes)
|
||||
(local.get $temp_64))
|
||||
|
||||
;; do it all in one step instead
|
||||
(f64.store
|
||||
(local.get $idx_bytes)
|
||||
(call $cos
|
||||
(f64.load
|
||||
(local.get $idx_bytes))))
|
||||
|
||||
;; increment the loop counter
|
||||
(local.set $idx (i32.add (local.get $idx) (i32.const 1)))
|
||||
|
||||
;; stop the loop if the loop counter is equal the array length
|
||||
(br_if 1 (i32.eq (local.get $idx) (local.get $array_length)))
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(export "apply_cos64" (func $apply_cos64))
|
||||
)
|
||||
|
||||
```
|
@ -123,8 +123,8 @@ myHash[["Green"]] (* 2, use it *)
|
||||
myHash[["Green"]] := 5 (* 5, update it *)
|
||||
myHash[["Puce"]] := 3.5 (* 3.5, extend it *)
|
||||
KeyDropFrom[myHash, "Green"] (* Wipes out key Green *)
|
||||
Keys[myHash] (* {Red} *)
|
||||
Values[myHash] (* {1} *)
|
||||
Keys[myHash] (* {Red, Puce} *)
|
||||
Values[myHash] (* {1, 3.5} *)
|
||||
|
||||
(* And you can't do any demo of Wolfram without showing this off *)
|
||||
Manipulate[y^2, {y, 0, 20}] (* Return a reactive user interface that displays y^2
|
||||
|
@ -38,6 +38,8 @@ however: 'A string, enclosed in quotes.'
|
||||
'Keys can be quoted too.': "Useful if you want to put a ':' in your key."
|
||||
single quotes: 'have ''one'' escape pattern'
|
||||
double quotes: "have many: \", \0, \t, \u263A, \x0d\x0a == \r\n, and more."
|
||||
# UTF-8/16/32 characters need to be encoded
|
||||
Superscript two: \u00B2
|
||||
|
||||
# Multiple-line strings can be written either as a 'literal block' (using |),
|
||||
# or a 'folded block' (using '>').
|
||||
|
@ -221,7 +221,7 @@ Delete dataset
|
||||
|
||||
```bash
|
||||
# Datasets cannot be deleted if they have any snapshots
|
||||
zfs destroy tank/root/home
|
||||
$ zfs destroy tank/root/home
|
||||
```
|
||||
|
||||
Get / set properties of a dataset
|
||||
@ -294,6 +294,7 @@ tank/home/sarlalian@now 0 - 259M -
|
||||
tank/home/alice@now 0 - 156M -
|
||||
tank/home/bob@now 0 - 156M -
|
||||
...
|
||||
```
|
||||
|
||||
Destroy snapshots
|
||||
|
||||
@ -313,13 +314,13 @@ Renaming Snapshots
|
||||
$ zfs rename tank/home/sarlalian@now tank/home/sarlalian@today
|
||||
$ zfs rename tank/home/sarlalian@now today
|
||||
|
||||
# zfs rename -r tank/home@now @yesterday
|
||||
$ zfs rename -r tank/home@now @yesterday
|
||||
```
|
||||
|
||||
Accessing snapshots
|
||||
|
||||
```bash
|
||||
# CD Into a snapshot directory
|
||||
# CD into a snapshot directory
|
||||
$ cd /home/.zfs/snapshot/
|
||||
```
|
||||
|
||||
@ -335,11 +336,11 @@ $ zfs send tank/home/sarlalian@now | zfs recv backups/home/sarlalian
|
||||
# Send a snapshot to a remote host
|
||||
$ zfs send tank/home/sarlalian@now | ssh root@backup_server 'zfs recv tank/home/sarlalian'
|
||||
|
||||
# Send full dataset with snapshos to new host
|
||||
# Send full dataset with snapshots to new host
|
||||
$ zfs send -v -R tank/home@now | ssh root@backup_server 'zfs recv tank/home'
|
||||
```
|
||||
|
||||
Cloneing Snapshots
|
||||
Cloning Snapshots
|
||||
|
||||
```bash
|
||||
# Clone a snapshot
|
||||
|
223
zh-cn/lambda-calculus-cn.html.markdown
Normal file
223
zh-cn/lambda-calculus-cn.html.markdown
Normal file
@ -0,0 +1,223 @@
|
||||
---
|
||||
category: Algorithms & Data Structures
|
||||
name: Lambda Calculus
|
||||
lang: zh-cn
|
||||
contributors:
|
||||
- ["Max Sun", "http://github.com/maxsun"]
|
||||
- ["Yan Hui Hang", "http://github.com/yanhh0"]
|
||||
translators:
|
||||
- ["Maoyin Sun", "https://github.com/simonmysun"]
|
||||
---
|
||||
|
||||
# Lambda 演算
|
||||
|
||||
Lambda 演算(lambda calculus, λ-calculus),
|
||||
最初由[阿隆佐·邱奇][]([Alonzo Church][])提出,
|
||||
是世界上最小的编程语言.
|
||||
尽管没有数字, 字符串, 布尔或者任何非函数的数据类型,
|
||||
lambda 演算仍可以表示任何图灵机.
|
||||
|
||||
[阿隆佐·邱奇]: https://zh.wikipedia.org/wiki/%E9%98%BF%E9%9A%86%E4%BD%90%C2%B7%E9%82%B1%E5%A5%87
|
||||
[Alonzo Church]: https://en.wikipedia.org/wiki/Alonzo_Church
|
||||
|
||||
Lambda 演算由三种元素组成: **变量**(variables)、**函数**(functions)和**应用**(applications)。
|
||||
|
||||
| 名称 | 语法 | 示例 | 解释 |
|
||||
|------|----------------------|-----------|--------------------------------------------------|
|
||||
| 变量 | `<变量名>` | `x` | 一个名为"x"的变量 |
|
||||
| 函数 | `λ<参数>.<函数体>` | `λx.x` | 一个以"x"(前者)为参数、以"x"(后者)为函数体的函数 |
|
||||
| 应用 | `<函数><变量或函数>` | `(λx.x)a` | 以"a"为参数调用函数"λx.x" |
|
||||
|
||||
最基本的函数为恒等函数: `λx.x`, 它等价于`f(x) = x`.
|
||||
第一个"x"为函数的参数, 第二个为函数体.
|
||||
|
||||
## 自由变量和约束变量:
|
||||
|
||||
- 在函数`λx.x`中, "x"被称作约束变量因为它同时出现在函数体和函数参数中.
|
||||
- 在`λx.y`中, "y"被称作自由变量因为它没有被预先声明.
|
||||
|
||||
## 求值:
|
||||
|
||||
求值操作是通过[β-归约][]([β-Reduction][])完成的,
|
||||
它本质上是词法层面上的替换.
|
||||
|
||||
[β-归约]: https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97#'%22%60UNIQ--postMath-0000006F-QINU%60%22'-%E6%AD%B8%E7%B4%84
|
||||
[β-Reduction]: https://en.wikipedia.org/wiki/Lambda_calculus#Beta_reduction
|
||||
|
||||
当对表达式`(λx.x)a`求值时, 我们将函数体中所有出现的"x"替换为"a".
|
||||
|
||||
- `(λx.x)a`计算结果为: `a`
|
||||
- `(λx.y)a`计算结果为: `y`
|
||||
|
||||
你甚至可以创建高阶函数:
|
||||
|
||||
- `(λx.(λy.x))a`计算结果为: `λy.a`
|
||||
|
||||
尽管 lambda 演算传统上仅支持单个参数的函数,
|
||||
但我们可以通过一种叫作[柯里化][]([Currying][])的技巧创建多个参数的函数.
|
||||
|
||||
[柯里化]: https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96
|
||||
[Currying]: https://en.wikipedia.org/wiki/Currying
|
||||
|
||||
- `(λx.λy.λz.xyz)`等价于`f(x, y, z) = ((x y) z)`
|
||||
|
||||
有时`λxy.<body>`与`λx.λy.<body>`可以互换使用.
|
||||
|
||||
----
|
||||
|
||||
认识到传统的 **lambda 演算没有数字, 字符或者任何非函数的数据类型**很重要.
|
||||
|
||||
## 布尔逻辑:
|
||||
|
||||
在 lambda 演算中没有"真"或"假". 甚至没有 1 或 0.
|
||||
|
||||
作为替换:
|
||||
|
||||
`T`表示为: `λx.λy.x`
|
||||
|
||||
`F`表示为: `λx.λy.y`
|
||||
|
||||
首先, 我们可以定义一个"if"函数`λbtf`, 它当`b`为真时返回`t`,
|
||||
`b`为假时返回`f`
|
||||
|
||||
`IF`等价于: `λb.λt.λf.b t f`
|
||||
|
||||
通过`IF`, 我们可以定义基本的布尔逻辑运算符:
|
||||
|
||||
`a AND b`等价于: `λab.IF a b F`
|
||||
|
||||
`a OR b`等价于: `λab.IF a T b`
|
||||
|
||||
`NOT a`等价于: `λa.IF a F T`
|
||||
|
||||
*注意: `IF a b c`本质上指: `IF((a b) c)`*
|
||||
|
||||
## 数字:
|
||||
|
||||
尽管 lambda 演算中没有数字,
|
||||
我们还可以用[邱奇编码][]([Church numerals][])将数字嵌入到 lambda 演算中.
|
||||
|
||||
[邱奇编码]: https://zh.wikipedia.org/wiki/%E9%82%B1%E5%A5%87%E7%BC%96%E7%A0%81
|
||||
[Church numerals]: https://en.wikipedia.org/wiki/Church_encoding
|
||||
|
||||
对于任意数字 n: <code>n = λf.f<sup>n</sup></code> 所以:
|
||||
|
||||
`0 = λf.λx.x`
|
||||
|
||||
`1 = λf.λx.f x`
|
||||
|
||||
`2 = λf.λx.f(f x)`
|
||||
|
||||
`3 = λf.λx.f(f(f x))`
|
||||
|
||||
要增加一个邱奇数, 我们使用后继函数`S(n) = n + 1`:
|
||||
|
||||
`S = λn.λf.λx.f((n f) x)`
|
||||
|
||||
使用后继函数, 我们可以定义加法:
|
||||
|
||||
`ADD = λab.(a S)b`
|
||||
|
||||
**挑战**: 试定义乘法函数!
|
||||
|
||||
## 变得更小: SKI, SK 和 Iota
|
||||
|
||||
### SKI 组合子演算
|
||||
|
||||
令 S, K, I 为下列函数:
|
||||
|
||||
`I x = x`
|
||||
|
||||
`K x y = x`
|
||||
|
||||
`S x y z = x z (y z)`
|
||||
|
||||
我们可以将 lambda 演算中的表达式转换为 SKI 组合子演算中的表达式:
|
||||
|
||||
1. `λx.x = I`
|
||||
2. `λx.c = Kc`
|
||||
3. `λx.(y z) = S (λx.y) (λx.z)`
|
||||
|
||||
以邱奇数 2 为例:
|
||||
|
||||
`2 = λf.λx.f(f x)`
|
||||
|
||||
对于里面的部分 `λx.f(f x)`:
|
||||
|
||||
```
|
||||
λx.f(f x)
|
||||
= S (λx.f) (λx.(f x)) (case 3)
|
||||
= S (K f) (S (λx.f) (λx.x)) (case 2, 3)
|
||||
= S (K f) (S (K f) I) (case 2, 1)
|
||||
```
|
||||
|
||||
所以:
|
||||
|
||||
```
|
||||
2
|
||||
= λf.λx.f(f x)
|
||||
= λf.(S (K f) (S (K f) I))
|
||||
= λf.((S (K f)) (S (K f) I))
|
||||
= S (λf.(S (K f))) (λf.(S (K f) I)) (case 3)
|
||||
```
|
||||
|
||||
对于第一个参数`λf.(S (K f))`有:
|
||||
|
||||
```
|
||||
λf.(S (K f))
|
||||
= S (λf.S) (λf.(K f)) (case 3)
|
||||
= S (K S) (S (λf.K) (λf.f)) (case 2, 3)
|
||||
= S (K S) (S (K K) I) (case 2, 3)
|
||||
```
|
||||
|
||||
对于第二个参数`λf.(S (K f) I)`有:
|
||||
|
||||
```
|
||||
λf.(S (K f) I)
|
||||
= λf.((S (K f)) I)
|
||||
= S (λf.(S (K f))) (λf.I) (case 3)
|
||||
= S (S (λf.S) (λf.(K f))) (K I) (case 2, 3)
|
||||
= S (S (K S) (S (λf.K) (λf.f))) (K I) (case 1, 3)
|
||||
= S (S (K S) (S (K K) I)) (K I) (case 1, 2)
|
||||
```
|
||||
|
||||
综上:
|
||||
|
||||
```
|
||||
2
|
||||
= S (λf.(S (K f))) (λf.(S (K f) I))
|
||||
= S (S (K S) (S (K K) I)) (S (S (K S) (S (K K) I)) (K I))
|
||||
```
|
||||
|
||||
如果展开这个表达式, 我们最终又会得到邱奇数 2 的相同的表达式.
|
||||
|
||||
### SK 组合子演算
|
||||
|
||||
SKI 组合子演算还可以进一步简化. 我们可以通过`I = SKK`移除 I 组合子.
|
||||
我们可以将所有的 `I` 替换为 `SKK`.
|
||||
|
||||
### ι 组合子
|
||||
|
||||
SK 组合子仍不是最简的. 定义:
|
||||
|
||||
```
|
||||
ι = λf.((f S) K)
|
||||
```
|
||||
|
||||
我们有:
|
||||
|
||||
```
|
||||
I = ιι
|
||||
K = ι(ιI) = ι(ι(ιι))
|
||||
S = ι(K) = ι(ι(ι(ιι)))
|
||||
```
|
||||
|
||||
## 更多阅读:
|
||||
|
||||
1. [A Tutorial Introduction to the Lambda Calculus](http://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf)(英文)
|
||||
2. [Cornell CS 312 Recitation 26: The Lambda Calculus](https://courses.cs.cornell.edu/cs312/2008sp/recitations/rec26.html)(英文)
|
||||
3. [Wikipedia - Lambda Calculus](https://en.wikipedia.org/wiki/Lambda_calculus)(英文)
|
||||
4. [Wikipedia - SKI combinator calculus](https://en.wikipedia.org/wiki/SKI_combinator_calculus)(英文)
|
||||
5. [Wikipedia - Iota and Jot](https://en.wikipedia.org/wiki/Iota_and_Jot)(英文)
|
||||
6. [λ演算 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/SKI%E7%BB%84%E5%90%88%E5%AD%90%E6%BC%94%E7%AE%97)
|
||||
7. [SKI组合子演算 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/SKI%E7%BB%84%E5%90%88%E5%AD%90%E6%BC%94%E7%AE%97)
|
@ -5,6 +5,7 @@ contributors:
|
||||
translators:
|
||||
- ["Fangzhou Chen","https://github.com/FZSS"]
|
||||
- ["Luffy Zhong", "https://github.com/mengzhongshi"]
|
||||
- ["Yuchen Liu", "https://github.com/smallg0at"]
|
||||
filename: learnmarkdown-cn.md
|
||||
lang: zh-cn
|
||||
---
|
||||
@ -46,6 +47,16 @@ Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown
|
||||
##### 这是一个 <h5>
|
||||
###### 这是一个 <h6>
|
||||
```
|
||||
|
||||
实际效果(最终显示时会因设置而看起来不同):
|
||||
# 这是一个
|
||||
## 这也是一个
|
||||
### 这还是一个
|
||||
#### 这依旧是一个
|
||||
##### 这真的是一个
|
||||
###### 这...是一个
|
||||
|
||||
|
||||
对于 `<h1>` 和 `<h2>` 元素,Markdown 额外提供了两种添加方式。
|
||||
|
||||
```md
|
||||
@ -58,7 +69,7 @@ Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown
|
||||
|
||||
## 文本样式
|
||||
|
||||
文本的斜体,粗体在 Markdown 中可以轻易实现。
|
||||
文本的*斜体*,**粗体**在 Markdown 中可以轻易实现。
|
||||
|
||||
```md
|
||||
*此文本为斜体。*
|
||||
@ -72,7 +83,7 @@ __此文本也是__
|
||||
*__这个也是!__*
|
||||
```
|
||||
|
||||
GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以使用删除线:
|
||||
GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以使用~~删除线~~:
|
||||
|
||||
```md
|
||||
~~此文本为删除线效果。~~
|
||||
@ -80,6 +91,7 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以
|
||||
## 段落
|
||||
|
||||
段落由一个句子或是多个中间没有空行的句子组成,每个段落由一个或是多个空行分隔开来。
|
||||
(注:部分解析器有无需空行就能换行的设置,这个主要看个人喜好)
|
||||
|
||||
```md
|
||||
这是第一段落. 这句话在同一个段落里,好玩么?
|
||||
@ -92,7 +104,9 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以
|
||||
```
|
||||
|
||||
如果你想插入一个 `<br />` 标签,你可以在段末加入两个以上的空格,然后另起一
|
||||
段。(译者注:试了一下,很多解析器,并不需要空两个空格,直接换行就会添加一个`<br />`)
|
||||
段。
|
||||
|
||||
(译者注:试了一下,很多解析器,并不需要空两个空格,直接换行就会添加一个`<br />`)
|
||||
|
||||
```md
|
||||
此段落结尾有两个空格(选中以显示)。
|
||||
@ -102,6 +116,8 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以
|
||||
|
||||
段落引用可由 `>` 字符轻松实现。
|
||||
|
||||
> 对的很轻松
|
||||
|
||||
```md
|
||||
> 这是一个段落引用。 你可以
|
||||
> 手动断开你的句子,然后在每句句子前面添加 `>` 字符。或者让你的句子变得很长,以至于他们自动得换行。
|
||||
@ -113,7 +129,7 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以
|
||||
```
|
||||
|
||||
## 列表
|
||||
无序列表可由星号,加号或者减号来创建
|
||||
- 无序列表可由星号,加号或者减号来创建
|
||||
|
||||
```md
|
||||
* 项目
|
||||
@ -172,6 +188,7 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以
|
||||
下面这个选择框将会是选中状态
|
||||
- [x] 这个任务已经完成
|
||||
```
|
||||
- [ ] 你看完了这个任务(注:此选择框是无法直接更改的,即禁用状态。)
|
||||
|
||||
## 代码块
|
||||
|
||||
@ -217,7 +234,10 @@ end
|
||||
---
|
||||
- - -
|
||||
****************
|
||||
|
||||
下面这个就是示例
|
||||
```
|
||||
---
|
||||
|
||||
## 链接
|
||||
|
||||
@ -294,33 +314,45 @@ Markdown同样支持引用形式的链接
|
||||
我希望 *将这段文字置于星号之间* 但是我不希望它被
|
||||
斜体化, 这么做: \*这段置文字于星号之间\*。
|
||||
```
|
||||
对比一下:*将这段文字置于星号之间* 和 \*将这段文字置于星号之间\*
|
||||
|
||||
### 键盘上的功能键
|
||||
|
||||
在 GitHub 的 Markdown中,你可以使用 `<kbd>` 标签来表示功能键。
|
||||
在 GitHub 的 Markdown 中,你可以使用 `<kbd>` 标签来表示功能键。
|
||||
|
||||
```md
|
||||
你的电脑死机了?试试
|
||||
<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd>
|
||||
```
|
||||
<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd>
|
||||
|
||||
(译注:可能由于网站本身样式问题,效果不明显)
|
||||
|
||||
### 表格
|
||||
|
||||
表格只被 GitHub 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: (译者注:其实现在大部分markdown都已经支持)
|
||||
下面示例的表格长这样:
|
||||
|
||||
| 第一列 | 第二列 | 第三列 |
|
||||
| :----------- | :-------: | ----------: |
|
||||
| 我是左对齐 | 居个中 | 右对齐 |
|
||||
| 注意 | 冒 | 号 |
|
||||
|
||||
工整一点的写法是这样的:
|
||||
|
||||
```md
|
||||
| 第一列 | 第二列 | 第三列 |
|
||||
| :--------- | :------: | ----------: |
|
||||
| 左对齐 | 居个中 | 右对齐 |
|
||||
| 某某某 | 某某某 | 某某某 |
|
||||
| :----------- | :-------: | ----------: |
|
||||
| 我是左对齐 | 居个中 | 右对齐 |
|
||||
| 注意 | 冒 | 号 |
|
||||
```
|
||||
|
||||
或者, 同样的
|
||||
好吧,强行对齐字符是很难的。但是,至少比下面这种写法好一点——
|
||||
|
||||
```md
|
||||
第一列 | 第二列 | 第三列
|
||||
我是超级超级长的第一列 | 第二列 | 第三列
|
||||
:-- | :-: | --:
|
||||
这太丑了 | 药不能 | 停
|
||||
这真的太丑了 | 药不能 | 停!!!!
|
||||
```
|
||||
真的是*看着令人头晕*
|
||||
|
||||
|
||||
更多信息, 请于[此处](http://daringfireball.net/projects/Markdown/syntax)参见 John Gruber 关于语法的官方帖子,及于[此处](https://github.com/adam-p/Markdown-here/wiki/Markdown-Cheatsheet) 参见 Adam Pritchard 的摘要笔记。
|
||||
|
105
zh-cn/sql.html.markdown
Normal file
105
zh-cn/sql.html.markdown
Normal file
@ -0,0 +1,105 @@
|
||||
---
|
||||
language: SQL
|
||||
filename: learnsql.sql
|
||||
contributors:
|
||||
- ["Bob DuCharme", "http://bobdc.com/"]
|
||||
translators:
|
||||
- ["Shuxin Shu", "https://github.com/NamelessAshone"]
|
||||
lang: zh-cn
|
||||
---
|
||||
|
||||
结构化查询语言(SQL)是一个ISO标准语言,用于创建和管理数据库,
|
||||
这种数据库存储一系列表。不同的实现通常会添加特有的语言扩展;
|
||||
[不同SQL实现的比较(Comparison of different SQL implementat-
|
||||
ions)](http://troels.arvin.dk/db/rdbms/)是一份很好的产品差
|
||||
异参考文档。
|
||||
|
||||
不同的实现通常会提供一个命令行用于交互式键入命令和显示输出,
|
||||
同时这些实现也会提供一种执行脚本文件的方法。(如何退出命令行
|
||||
就是就是SQL中尚未被标准化部分的一个典型例子,绝大多数SQL实
|
||||
现支持关键字QUIT、EXIT或者两者。)
|
||||
|
||||
本文的实例命令假设你已经加载了[github](https://github.com/datacharmer/test_db)上的[MySQL示例员工数据库](https://dev.mysql.com/doc/employee/en/)。
|
||||
运行脚本的语法取决于你使用的SQL实现。通常是一个命令行工具。
|
||||
|
||||
```sql
|
||||
|
||||
-- 注释以两个连字符开始。命令以分号结束。
|
||||
|
||||
-- SQL关键字大小写不敏感。在下文的示例命令中关键字大写,
|
||||
-- 因为大写更容易区分数据库、表和列名。
|
||||
|
||||
-- 创建和删除一个数据库。数据库名和表名是大小写敏感的。
|
||||
CREATE DATABASE someDatabase;
|
||||
DROP DATABASE someDatabase;
|
||||
|
||||
-- 列出可用的数据库。
|
||||
SHOW DATABASES;
|
||||
|
||||
-- 使用某个已经存在的数据库
|
||||
USE employees;
|
||||
|
||||
-- 从当前的departments表,选择所有的行和列
|
||||
-- 解释器的默认行为是将结果打印在屏幕上。
|
||||
SELECT * FROM departments;
|
||||
|
||||
-- 检索departments表中所有的行,但只取dept_no和dept_name列。
|
||||
-- 一条命令可以跨越多行
|
||||
SELECT dept_no,
|
||||
dept_name FROM departments;
|
||||
|
||||
-- 检索departments表中所有的行,但是只输出5行。
|
||||
SELECT * FROM departments LIMIT 5;
|
||||
|
||||
-- 检索departments表中dept_name列包含子串'en'的行。
|
||||
SELECT dept_name FROM departments WHERE dept_name LIKE '%en%';
|
||||
|
||||
-- 检索departmnets表中所有dept_name列值为'S'开头并且'S'后接4个字符的行。
|
||||
SELECT * FROM departments WHERE dept_name LIKE 'S____';
|
||||
|
||||
-- 检索title表中所有行,不显示重复的行。
|
||||
SELECT DISTINCT title FROM titles;
|
||||
|
||||
-- 和上面的查询相同,但是以title的值排序(大小写敏感)。
|
||||
SELECT DISTINCT title FROM titles ORDER BY title;
|
||||
|
||||
-- 计算departments表的总行数。
|
||||
SELECT COUNT(*) FROM departments;
|
||||
|
||||
-- 计算departments表中dept_name列以'en'字段开头的行的数量。
|
||||
SELECT COUNT(*) FROM departments WHERE dept_name LIKE '%en%';
|
||||
|
||||
-- 不同表中信息的JOIN: titles表显示谁有什么工作,员工编号,
|
||||
-- 入职离职时间。检索这些信息,但是使用员工编号作为employees表
|
||||
-- 的交叉引用,而不是直接使用员工编号,来获得每个员工的名和姓。
|
||||
-- (同时只取10行)
|
||||
|
||||
SELECT employees.first_name, employees.last_name,
|
||||
titles.title, titles.from_date, titles.to_date
|
||||
FROM titles INNER JOIN employees ON
|
||||
employees.emp_no = titles.emp_no LIMIT 10;
|
||||
|
||||
-- 列出所有数据库中所有的表。不同实现通常提供各自的快捷命令
|
||||
-- 来列出当前使用数据库的所有表。
|
||||
SELECT * FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_TYPE='BASE TABLE';
|
||||
|
||||
-- 在当前使用的数据库中,创建一个名为tablename1的表,包含下
|
||||
-- 述两列。许多其它选项可用于定制列,比如列的数据类型。
|
||||
CREATE TABLE tablename1 (fname VARCHAR(20), lname VARCHAR(20));
|
||||
|
||||
-- 向tablename1表插入一行数据。假设该表已经定义并且接受这些值。
|
||||
INSERT INTO tablename1 VALUES('Richard','Mutt');
|
||||
|
||||
-- 更新tablename1表中lname为'Mutt'的行fname的值改为'John'。
|
||||
UPDATE tablename1 SET fname='John' WHERE lname='Mutt';
|
||||
|
||||
-- 删除tablename1表lname列以'M'开头的行。
|
||||
DELETE FROM tablename1 WHERE lname like 'M%';
|
||||
|
||||
-- 删除tablename1表的所有行,留下空表。
|
||||
DELETE FROM tablename1;
|
||||
|
||||
-- 删除整个tablename1表。
|
||||
DROP TABLE tablename1;
|
||||
```
|
Loading…
Reference in New Issue
Block a user