diff options
-rw-r--r-- | c.html.markdown | 678 | ||||
-rw-r--r-- | es-es/c-es.html.markdown | 2 | ||||
-rw-r--r-- | fr-fr/ruby-fr.html.markdown | 407 | ||||
-rw-r--r-- | java.html.markdown | 4 | ||||
-rw-r--r-- | objective-c.html.markdown | 4 | ||||
-rw-r--r-- | pt-br/ruby-pt.html.markdown | 384 | ||||
-rw-r--r-- | ru-ru/clojure-ru.html.markdown | 426 | ||||
-rw-r--r-- | ruby-ecosystem.html.markdown | 2 | ||||
-rw-r--r-- | ruby.html.markdown | 6 | ||||
-rw-r--r-- | zh-cn/go-zh.html.markdown | 279 |
10 files changed, 1906 insertions, 286 deletions
diff --git a/c.html.markdown b/c.html.markdown index 2b50efa0..8e16837c 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -1,23 +1,25 @@ --- -name: c -category: language -language: c -filename: learnc.c -contributors: - - ["Adam Bard", "http://adambard.com/"] +- name: c +- category: language +- language: c +- filename: learnc.c +- contributors: + - [Adam Bard](http://adambard.com/) + - [Árpád Goretity](http://twitter.com/H2CO3_iOS) + --- -Ah, C. Still the language of modern high-performance computing. +Ah, C. Still **the** language of modern high-performance computing. C is the lowest-level language most programmers will ever use, but it more than makes up for it with raw speed. Just be aware of its manual memory management and C will take you as far as you need to go. ```c -// Single-line comments start with // +// Single-line comments start with // - only available in C99 and later. /* -Multi-line comments look like this. +Multi-line comments look like this. They work in C89 as well. */ // Import headers with #include @@ -25,6 +27,17 @@ Multi-line comments look like this. #include <stdio.h> #include <string.h> +// file names between <angle brackets> are headers from the C standard library. +// They are searched for by the preprocessor in the system include paths +// (usually /usr/lib on Unices, can be controlled with the -I<dir> option if you are using GCC or clang.) +// For your own headers, use double quotes instead of angle brackets: +#include "my_header.h" + +// The C preprocessor introduces an almost fully-featured macro language. It's useful, but +// it can be confusing (and what's even worse, it can be misused). Read the +// Wikipedia article on the C preprocessor for further information: +// http://en.wikipedia.org/wiki/C_preprocessor + // Declare function signatures in advance in a .h file, or at the top of // your .c file. void function_1(); @@ -33,264 +46,347 @@ void function_2(); // Your program's entry point is a function called // main with an integer return type. int main() { - -// print output using printf, for "print formatted" -// %d is an integer, \n is a newline -printf("%d\n", 0); // => Prints 0 -// All statements must end with a semicolon - -/////////////////////////////////////// -// Types -/////////////////////////////////////// - -// You have to declare variables before using them. A variable declaration -// requires you to specify its type; a variable's type determines its size -// in bytes. - -// ints are usually 4 bytes -int x_int = 0; - -// shorts are usually 2 bytes -short x_short = 0; - -// chars are guaranteed to be 1 byte -char x_char = 0; -char y_char = 'y'; // Char literals are quoted with '' - -// longs are often 4 to 8 bytes; long longs are guaranteed to be at least -// 64 bits -long x_long = 0; -long long x_long_long = 0; - -// floats are usually 32-bit floating point numbers -float x_float = 0.0; - -// doubles are usually 64-bit floating-point numbers -double x_double = 0.0; - -// Integral types may be unsigned. This means they can't be negative, but -// the maximum value of an unsigned variable is greater than the maximum -// signed value of the same size. -unsigned char ux_char; -unsigned short ux_short; -unsigned int ux_int; -unsigned long long ux_long_long; - -// Other than char, which is always 1 byte, these types vary in size depending -// on your machine. sizeof(T) gives you the size of a variable with type T in -// bytes so you can express the size of these types in a portable way. -// For example, -printf("%lu\n", sizeof(int)); // => 4 (on machines with 4-byte words) - -// Arrays must be initialized with a concrete size. -char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes -int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes - // (assuming 4-byte words) - - -// You can initialize an array to 0 thusly: -char my_array[20] = {0}; - -// Indexing an array is like other languages -- or, -// rather, other languages are like C -my_array[0]; // => 0 - -// Arrays are mutable; it's just memory! -my_array[1] = 2; -printf("%d\n", my_array[1]); // => 2 - -// Strings are just arrays of chars terminated by a NUL (0x00) byte, -// represented in strings as the special character '\0'. -// (We don't have to include the NUL byte in string literals; the compiler -// inserts it at the end of the array for us.) -char a_string[20] = "This is a string"; -printf("%s\n", a_string); // %s formats a string - -/* -You may have noticed that a_string is only 16 chars long. -Char #17 is the NUL byte. -Chars #18, 19 and 20 have undefined values. -*/ - -printf("%d\n", a_string[16]); // => 0 - -/////////////////////////////////////// -// Operators -/////////////////////////////////////// - -int i1 = 1, i2 = 2; // Shorthand for multiple declaration -float f1 = 1.0, f2 = 2.0; - -// Arithmetic is straightforward -i1 + i2; // => 3 -i2 - i1; // => 1 -i2 * i1; // => 2 -i1 / i2; // => 0 (0.5, but truncated towards 0) - -f1 / f2; // => 0.5, plus or minus epsilon - -// Modulo is there as well -11 % 3; // => 2 - -// Comparison operators are probably familiar, but -// there is no boolean type in c. We use ints instead. -// 0 is false, anything else is true. (The comparison -// operators always return 0 or 1.) -3 == 2; // => 0 (false) -3 != 2; // => 1 (true) -3 > 2; // => 1 -3 < 2; // => 0 -2 <= 2; // => 1 -2 >= 2; // => 1 - -// Logic works on ints -!3; // => 0 (Logical not) -!0; // => 1 -1 && 1; // => 1 (Logical and) -0 && 1; // => 0 -0 || 1; // => 1 (Logical or) -0 || 0; // => 0 - -// Bitwise operators! -~0x0F; // => 0xF0 (bitwise negation) -0x0F & 0xF0; // => 0x00 (bitwise AND) -0x0F | 0xF0; // => 0xFF (bitwise OR) -0x04 ^ 0x0F; // => 0x0B (bitwise XOR) -0x01 << 1; // => 0x02 (bitwise left shift (by 1)) -0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) - -/////////////////////////////////////// -// Control Structures -/////////////////////////////////////// - -if (0) { - printf("I am never run\n"); -} else if (0) { - printf("I am also never run\n"); -} else { - printf("I print\n"); -} - -// While loops exist -int ii = 0; -while (ii < 10) { - printf("%d, ", ii++); // ii++ increments ii in-place, after using its value. -} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - -printf("\n"); - -int kk = 0; -do { - printf("%d, ", kk); -} while (++kk < 10); // ++kk increments kk in-place, before using its value -// => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - -printf("\n"); - -// For loops too -int jj; -for (jj=0; jj < 10; jj++) { - printf("%d, ", jj); -} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - -printf("\n"); - -/////////////////////////////////////// -// Typecasting -/////////////////////////////////////// - -// Every value in C has a type, but you can cast one value into another type -// if you want. - -int x_hex = 0x01; // You can assign vars with hex literals - -// Casting between types will attempt to preserve their numeric values -printf("%d\n", x_hex); // => Prints 1 -printf("%d\n", (short) x_hex); // => Prints 1 -printf("%d\n", (char) x_hex); // => Prints 1 - -// Types will overflow without warning -printf("%d\n", (char) 257); // => 1 (Max char = 255) - -// Integral types can be cast to floating-point types, and vice-versa. -printf("%f\n", (float)100); // %f formats a float -printf("%lf\n", (double)100); // %lf formats a double -printf("%d\n", (char)100.0); - -/////////////////////////////////////// -// Pointers -/////////////////////////////////////// - -// A pointer is a variable declared to store a memory address. Its declaration will -// also tell you the type of data it points to. You can retrieve the memory address -// of your variables, then mess with them. - -int x = 0; -printf("%p\n", &x); // Use & to retrieve the address of a variable -// (%p formats a pointer) -// => Prints some address in memory; - - -// Pointers start with * in their declaration -int *px, not_a_pointer; // px is a pointer to an int -px = &x; // Stores the address of x in px -printf("%p\n", px); // => Prints some address in memory -printf("%d, %d\n", (int)sizeof(px), (int)sizeof(not_a_pointer)); -// => Prints "8, 4" on 64-bit system - -// To retreive the value at the address a pointer is pointing to, -// put * in front to de-reference it. -printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of - -// You can also change the value the pointer is pointing to. -// We'll have to wrap the de-reference in parenthesis because -// ++ has a higher precedence than *. -(*px)++; // Increment the value px is pointing to by 1 -printf("%d\n", *px); // => Prints 1 -printf("%d\n", x); // => Prints 1 - -int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory -int xx; -for (xx=0; xx<20; xx++) { - x_array[xx] = 20 - xx; -} // Initialize x_array to 20, 19, 18,... 2, 1 - -// Declare a pointer of type int and initialize it to point to x_array -int* x_ptr = x_array; -// x_ptr now points to the first element in the array (the integer 20). -// This works because arrays are actually just pointers to their first element. - -// Arrays are pointers to their first element -printf("%d\n", *(x_ptr)); // => Prints 20 -printf("%d\n", x_array[0]); // => Prints 20 - -// Pointers are incremented and decremented based on their type -printf("%d\n", *(x_ptr + 1)); // => Prints 19 -printf("%d\n", x_array[1]); // => Prints 19 - -// You can also dynamically allocate contiguous blocks of memory with the -// standard library function malloc, which takes one integer argument -// representing the number of bytes to allocate from the heap. -int* my_ptr = (int*) malloc(sizeof(int) * 20); -for (xx=0; xx<20; xx++) { - *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here -} // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) - -// Dereferencing memory that you haven't allocated gives -// unpredictable results -printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? - -// When you're done with a malloc'd block of memory, you need to free it, -// or else no one else can use it until your program terminates -free(my_ptr); - -// Strings can be char arrays, but are usually represented as char -// pointers: -char* my_str = "This is my very own string"; - -printf("%c\n", *my_str); // => 'T' - -function_1(); + // print output using printf, for "print formatted" + // %d is an integer, \n is a newline + printf("%d\n", 0); // => Prints 0 + // All statements must end with a semicolon + + /////////////////////////////////////// + // Types + /////////////////////////////////////// + + // You have to declare variables before using them. A variable declaration + // requires you to specify its type; a variable's type determines its size + // in bytes. + + // ints are usually 4 bytes + int x_int = 0; + + // shorts are usually 2 bytes + short x_short = 0; + + // chars are guaranteed to be 1 byte + char x_char = 0; + char y_char = 'y'; // Char literals are quoted with '' + + // longs are often 4 to 8 bytes; long longs are guaranteed to be at least + // 64 bits + long x_long = 0; + long long x_long_long = 0; + + // floats are usually 32-bit floating point numbers + float x_float = 0.0; + + // doubles are usually 64-bit floating-point numbers + double x_double = 0.0; + + // Integral types may be unsigned. This means they can't be negative, but + // the maximum value of an unsigned variable is greater than the maximum + // signed value of the same size. + unsigned char ux_char; + unsigned short ux_short; + unsigned int ux_int; + unsigned long long ux_long_long; + + // Other than char, which is always 1 byte (but not necessarily 8 bits!), + // these types vary in size depending on your machine and compiler. + // sizeof(T) gives you the size of a variable with type T in + // bytes so you can express the size of these types in a portable way. + // sizeof(obj) yields the size of an actual expression (variable, literal, etc.). + // For example, + printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words) + + + // It's worth noting that if the argument of the `sizeof` operator is not a type but an expression, + // then its argument is not evaluated except VLAs (see below). Also, `sizeof()` is an operator, not a function, + // furthermore, the value it yields is a compile-time constant (except when used on VLAs, again.) + int a = 1; + size_t size = sizeof(a++); // a++ is not evaluated + printf("sizeof(a++) = %zu where a = %d\n", size, a); + // the above code prints "sizeof(a++) = 4 where a = 1" (on a usual 32-bit architecture) + + // Arrays must be initialized with a concrete size. + char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes + int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes + // (assuming 4-byte words) + + + // You can initialize an array to 0 thusly: + char my_array[20] = {0}; + + // Indexing an array is like other languages -- or, + // rather, other languages are like C + my_array[0]; // => 0 + + // Arrays are mutable; it's just memory! + my_array[1] = 2; + printf("%d\n", my_array[1]); // => 2 + + // In C99 (and as an optional feature in C11), variable-length arrays (VLAs) can be declared as well. + // The size of such an array need not be a compile time constant: + printf("Enter the array size: "); // ask the user for an array size + char buf[0x100]; + fgets(buf, sizeof buf, stdin); + size_t size = strtoul(buf, NULL, 10); // strtoul parses a string to an unsigned integer + int var_length_array[size]; // declare the VLA + printf("sizeof array = %zu\n", sizeof var_length_array); + + // A possible outcome of this program may be: + Enter the array size: 10 + sizeof array = 40 + + // Strings are just arrays of chars terminated by a NUL (0x00) byte, + // represented in strings as the special character '\0'. + // (We don't have to include the NUL byte in string literals; the compiler + // inserts it at the end of the array for us.) + char a_string[20] = "This is a string"; + printf("%s\n", a_string); // %s formats a string + + /* + You may have noticed that a_string is only 16 chars long. + Char #17 is the NUL byte. + Chars #18, 19 and 20 are 0 as well - if an initializer list (in this case, the string literal) + has less elements than the array it is initializing, then excess array elements are implicitly + initialized to zero. This is why int ar[10] = { 0 } works as expected intuitively. + */ + + printf("%d\n", a_string[16]); // => 0 + + // So string literals are strings enclosed within double quotes, but if we have characters + // between single quotes, that's a character literal. + // It's of type `int`, and *not* `char` (for historical reasons). + int cha = 'a'; // fine + char chb = 'a'; // fine too (implicit conversion from int to char - truncation) + + /////////////////////////////////////// + // Operators + /////////////////////////////////////// + + int i1 = 1, i2 = 2; // Shorthand for multiple declaration + float f1 = 1.0, f2 = 2.0; + + // Arithmetic is straightforward + i1 + i2; // => 3 + i2 - i1; // => 1 + i2 * i1; // => 2 + i1 / i2; // => 0 (0.5, but truncated towards 0) + + f1 / f2; // => 0.5, plus or minus epsilon - floating-point numbers and calculations are not exact + + // Modulo is there as well + 11 % 3; // => 2 + + // Comparison operators are probably familiar, but + // there is no boolean type in c. We use ints instead. + // (Or _Bool or bool in C99.) + // 0 is false, anything else is true. (The comparison + // operators always yield 0 or 1.) + 3 == 2; // => 0 (false) + 3 != 2; // => 1 (true) + 3 > 2; // => 1 + 3 < 2; // => 0 + 2 <= 2; // => 1 + 2 >= 2; // => 1 + + // C is not Python - comparisons don't chain. + int a = 1; + // WRONG: + int between_0_and_2 = 0 < a < 2; + // Correct: + int between_0_and_2 = 0 < a && a < 2; + + // Logic works on ints + !3; // => 0 (Logical not) + !0; // => 1 + 1 && 1; // => 1 (Logical and) + 0 && 1; // => 0 + 0 || 1; // => 1 (Logical or) + 0 || 0; // => 0 + + // Bitwise operators! + ~0x0F; // => 0xF0 (bitwise negation, "1's complement") + 0x0F & 0xF0; // => 0x00 (bitwise AND) + 0x0F | 0xF0; // => 0xFF (bitwise OR) + 0x04 ^ 0x0F; // => 0x0B (bitwise XOR) + 0x01 << 1; // => 0x02 (bitwise left shift (by 1)) + 0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) + + // Be careful when shifting signed integers - the following are all undefined behavior: + // - shifting into the sign bit of a signed integer (int a = 1 << 32) + // - left-shifting a negative number (int a = -1 << 2) + // - shifting by an offset which is more than or equal to the width of the type of the LHS: + // int a = 1 << 32; // UB if int is 32 bits wide + + /////////////////////////////////////// + // Control Structures + /////////////////////////////////////// + + if (0) { + printf("I am never run\n"); + } else if (0) { + printf("I am also never run\n"); + } else { + printf("I print\n"); + } + + // While loops exist + int ii = 0; + while (ii < 10) { + printf("%d, ", ii++); // ii++ increments ii in-place, after yielding its value ("postincrement"). + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + int kk = 0; + do { + printf("%d, ", kk); + } while (++kk < 10); // ++kk increments kk in-place, and yields the already incremented value ("preincrement") + // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // For loops too + int jj; + for (jj=0; jj < 10; jj++) { + printf("%d, ", jj); + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // branching with multiple choices: switch() + switch (some_integral_expression) { + case 0: // labels need to be integral *constant* epxressions + do_stuff(); + break; // if you don't break, control flow falls over labels - you usually don't want that. + case 1: + do_something_else(); + break; + default: + // if `some_integral_expression` didn't match any of the labels + fputs("error!\n", stderr); + exit(-1); + break; + } + + + /////////////////////////////////////// + // Typecasting + /////////////////////////////////////// + + // Every value in C has a type, but you can cast one value into another type + // if you want (with some constraints). + + int x_hex = 0x01; // You can assign vars with hex literals + + // Casting between types will attempt to preserve their numeric values + printf("%d\n", x_hex); // => Prints 1 + printf("%d\n", (short) x_hex); // => Prints 1 + printf("%d\n", (char) x_hex); // => Prints 1 + + // Types will overflow without warning + printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long) + // printf("%d\n", (unsigned char) 257); would be undefined behavior - `char' is usually signed + // on most modern systems, and signed integer overflow invokes UB. + // Also, for determining the maximal value of a `char`, a `signed char` and an `unisigned char`, + // respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from <limits.h> + + // Integral types can be cast to floating-point types, and vice-versa. + printf("%f\n", (float)100); // %f formats a float + printf("%lf\n", (double)100); // %lf formats a double + printf("%d\n", (char)100.0); + + /////////////////////////////////////// + // Pointers + /////////////////////////////////////// + + // A pointer is a variable declared to store a memory address. Its declaration will + // also tell you the type of data it points to. You can retrieve the memory address + // of your variables, then mess with them. + + int x = 0; + printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable + // (%p formats an object pointer of type void *) + // => Prints some address in memory; + + + // Pointers start with * in their declaration + int *px, not_a_pointer; // px is a pointer to an int + px = &x; // Stores the address of x in px + printf("%p\n", (void *)px); // => Prints some address in memory + printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); + // => Prints "8, 4" on a typical 64-bit system + + // To retreive the value at the address a pointer is pointing to, + // put * in front to de-reference it. + // Note: yes, it may be confusing that '*' is used for _both_ declaring a pointer and dereferencing it. + printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of + + // You can also change the value the pointer is pointing to. + // We'll have to wrap the de-reference in parenthesis because + // ++ has a higher precedence than *. + (*px)++; // Increment the value px is pointing to by 1 + printf("%d\n", *px); // => Prints 1 + printf("%d\n", x); // => Prints 1 + + int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory + int xx; + for (xx = 0; xx < 20; xx++) { + x_array[xx] = 20 - xx; + } // Initialize x_array to 20, 19, 18,... 2, 1 + + // Declare a pointer of type int and initialize it to point to x_array + int* x_ptr = x_array; + // x_ptr now points to the first element in the array (the integer 20). + // This works because arrays often decay into pointers to their first element. + // For example, when an array is passed to a function or is assigned to a pointer, + // it decays into (implicitly converted to) a pointer. + // Exceptions: when the array is the argument of the `&` (address-od) operator: + int arr[10]; + int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`! It's of type "pointer to array" (of ten `int`s). + // or when the array is a string literal used for initializing a char array: + char arr[] = "foobarbazquirk"; + // or when it's the argument of the `sizeof` or `alignof` operator: + int arr[10]; + int *ptr = arr; // equivalent with int *ptr = &arr[0]; + printf("%zu %zu\n", sizeof arr, sizeof ptr); // probably prints "40, 4" or "40, 8" + + + // Pointers are incremented and decremented based on their type + // (this is called pointer arithmetic) + printf("%d\n", *(x_ptr + 1)); // => Prints 19 + printf("%d\n", x_array[1]); // => Prints 19 + + // You can also dynamically allocate contiguous blocks of memory with the + // standard library function malloc, which takes one argument of type size_t + // representing the number of bytes to allocate (usually from the heap, although this + // may not be true on e. g. embedded systems - the C standard says nothing about it). + int *my_ptr = malloc(sizeof(*my_ptr) * 20); + for (xx = 0; xx < 20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here, and it's also more readable + } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) + + // Dereferencing memory that you haven't allocated gives + // "unpredictable results" - the program is said to invoke "undefined behavior" + printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? It may even crash. + + // When you're done with a malloc'd block of memory, you need to free it, + // or else no one else can use it until your program terminates + // (this is called a "memory leak"): + free(my_ptr); + + // Strings are arrays of char, but they are usually represented as a + // pointer-to-char (which is a pointer to the first element of the array). + // It's good practice to use `const char *' when referring to a string literal, + // since string literals shall not be modified (i. e. "foo"[0] = 'a' is ILLEGAL.) + const char *my_str = "This is my very own string literal"; + printf("%c\n", *my_str); // => 'T' + + // This is not the case if the string is an array (potentially initialized with a string literal) + // that resides in writable memory, as in: + char foo[] = "foo"; + foo[0] = 'a'; // this is legal, foo now contains "aoo" + + function_1(); } // end main function /////////////////////////////////////// @@ -300,7 +396,8 @@ function_1(); // Function declaration syntax: // <return type> <function name>(<args>) -int add_two_ints(int x1, int x2){ +int add_two_ints(int x1, int x2) +{ return x1 + x2; // Use return to return a value } @@ -312,10 +409,12 @@ Example: in-place string reversal */ // A void function returns no value -void str_reverse(char* str_in){ +void str_reverse(char *str_in) +{ char tmp; - int ii=0, len = strlen(str_in); // Strlen is part of the c standard library - for(ii=0; ii<len/2; ii++){ + int ii = 0; + size_t len = strlen(str_in); // `strlen()` is part of the c standard library + for (ii = 0; ii < len / 2; ii++) { tmp = str_in[ii]; str_in[ii] = str_in[len - ii - 1]; // ii-th char from end str_in[len - ii - 1] = tmp; @@ -336,15 +435,20 @@ printf("%s\n", c); // => ".tset a si sihT" typedef int my_type; my_type my_type_var = 0; -// Structs are just collections of data +// Structs are just collections of data, the members are allocated sequentially, in the order they are written: struct rectangle { int width; int height; }; +// it's generally not true that sizeof(struct rectangle) == sizeof(int) + sizeof(int) due to +// potential padding between the structure members (this is for alignment reasons. Probably won't +// happen if all members are of the same type, but watch out! +// See http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member +// for further information. -void function_1(){ - +void function_1() +{ struct rectangle my_rec; // Access struct members with . @@ -352,22 +456,29 @@ void function_1(){ my_rec.height = 20; // You can declare pointers to structs - struct rectangle* my_rec_ptr = &my_rec; + struct rectangle *my_rec_ptr = &my_rec; // Use dereferencing to set struct pointer members... (*my_rec_ptr).width = 30; - // ... or use the -> shorthand + // ... or even better: prefer the -> shorthand for the sake of readability my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10; } // You can apply a typedef to a struct for convenience typedef struct rectangle rect; -int area(rect r){ +int area(rect r) +{ return r.width * r.height; } +// if you have large structs, you can pass them "by pointer" to avoid copying the whole struct: +int area(const rect *r) +{ + return r->width * r->height; +} + /////////////////////////////////////// // Function pointers /////////////////////////////////////// @@ -379,10 +490,11 @@ However, definition syntax may be initially confusing. Example: use str_reverse from a pointer */ -void str_reverse_through_pointer(char * str_in) { +void str_reverse_through_pointer(char *str_in) { // Define a function pointer variable, named f. void (*f)(char *); // Signature should exactly match the target function. f = &str_reverse; // Assign the address for the actual function (determined at runtime) + // f = str_reverse; would work as well - functions decay into pointers, similar to arrays (*f)(str_in); // Just calling the function through the pointer // f(str_in); // That's an alternative but equally valid syntax for calling it. } @@ -403,7 +515,15 @@ typedef void (*my_fnp_type)(char *); ## Further Reading Best to find yourself a copy of [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language) +It is *the* book about C, written by the creators of C. Be careful, though - it's ancient and it contains some +inaccuracies (well, ideas that are not considered good anymore) or now-changed practices. + +Another good resource is [Learn C the hard way](http://c.learncodethehardway.org/book/). + +If you have a question, read the [compl.lang.c Frequently Asked Questions](http://c-faq.com). -Another good resource is [Learn C the hard way](http://c.learncodethehardway.org/book/) +It's very important to use proper spacing, indentation and to be consistent with your coding style in general. +Readable code is better than clever code and fast code. For a good, sane coding style to adopt, see the +[Linux kernel coding stlye](https://www.kernel.org/doc/Documentation/CodingStyle). Other than that, Google is your friend. diff --git a/es-es/c-es.html.markdown b/es-es/c-es.html.markdown index b109f761..5d3aae0c 100644 --- a/es-es/c-es.html.markdown +++ b/es-es/c-es.html.markdown @@ -284,7 +284,7 @@ for (xx=0; xx<20; xx++) { // impredecibles printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? -// Cuando hallas acabado con el bloque de memoría malloc, necesitas +// Cuando hayas acabado con el bloque de memoría malloc, necesitas // liberarlo o sino nadie más podrá usarlo hasta que tu programa se cierre free(my_ptr); diff --git a/fr-fr/ruby-fr.html.markdown b/fr-fr/ruby-fr.html.markdown new file mode 100644 index 00000000..5efb2f3c --- /dev/null +++ b/fr-fr/ruby-fr.html.markdown @@ -0,0 +1,407 @@ +--- +language: ruby +filename: learnruby-fr.rb +contributors: + - ["David Underwood", "http://theflyingdeveloper.com"] + - ["Joel Walden", "http://joelwalden.net"] + - ["Luke Holder", "http://twitter.com/lukeholder"] + - ["Tristan Hume", "http://thume.ca/"] + - ["Nick LaMuro", "https://github.com/NickLaMuro"] +translators: + - ["Geoffrey Roguelon", "https://github.com/GRoguelon"] + - ["Nami-Doc", "https://github.com/Nami-Doc"] +lang: fr-fr +--- + +```ruby +# Ceci est un commentaire + +=begin +Ceci est un commentaire multiligne +Personne ne les utilise +Vous devriez en faire de même +=end + +# Tout d'abord : Tout est un objet. + +# Les nombres sont des objets + +3.class #=> Fixnum + +3.to_s #=> "3" + +# Les opérateurs de base +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 + +# Les opérateurs sont juste des raccourcis +# pour appeler une méthode sur un objet +1.+(3) #=> 4 +10.* 5 #=> 50 + +# Les valeurs spéciales sont des objets +nil # Nul +true # Vrai +false # Faux + +nil.class #=> NilClass +true.class #=> TrueClass +false.class #=> FalseClass + +# Égalité +1 == 1 #=> true +2 == 1 #=> false + +# Inégalité +1 != 1 #=> false +2 != 1 #=> true +!true #=> false +!false #=> true + +# à part false lui-même, nil est la seule autre valeur 'false' + +!nil #=> true +!false #=> true +!0 #=> false + +# Plus de comparaisons +1 < 10 #=> true +1 > 10 #=> false +2 <= 2 #=> true +2 >= 2 #=> true + +# Les chaînes de caractères sont des objets + +'Je suis une chaîne de caractères'.class #=> String +"Je suis également une chaîne de caractères".class #=> String + +placeholder = "utiliser l'interpolation de chaîne de caractères" +"Je peux #{placeholder} quand j'utilise les guillemets" +#=> "Je peux utiliser l'interpolation de chaîne de caractères quand j'utilise les guillemets" + +# Affichez un message +puts "J'affiche à l'écran!" + +# Variables +x = 25 #=> 25 +x #=> 25 + +# Notez que l'affectation retourne la valeur affectée. +# Cela signifie que vous pouvez affecter plusieurs fois de suite : + +x = y = 10 #=> 10 +x #=> 10 +y #=> 10 + +# Par convention, utilisez la notation underscore +# pour nommer les variables +snake_case = true + +# Utilisez des noms de variable explicites +path_to_project_root = '/nom/correct/' +path = '/mauvais/nom/' + +# Symboles (aussi des objets) +# Les symboles sont immuables, constants, +# réutilisables et représentés en interne +# par une valeur entière. Ils sont souvent +# utilisés à la place des chaînes de caractères +# pour transmettre efficacement des valeurs +# spécifiques ou significatives + +:pending.class #=> Symbol + +status = :pending + +status == :pending #=> true + +status == 'pending' #=> false + +status == :approved #=> false + +# Tableaux + +# Ceci est un tableau +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] + +# Les tableaux contiennent différents types d'élément. + +[1, "hello", false] #=> [1, "hello", false] + +# Les tableaux peuvent être indexés +# Du début +array[0] #=> 1 +array[12] #=> nil + +# Comme les opérateurs, la syntaxe [var] est juste un raccourci +# pour appeler la méthode [] d'un objet +array.[] 0 #=> 1 +array.[] 12 #=> nil + +# Depuis la fin +array[-1] #=> 5 + +# Avec un index de début et de fin +array[2, 4] #=> [3, 4, 5] + +# Ou avec un intervalle +array[1..3] #=> [2, 3, 4] + +# Ajoutez un élément au tableau comme ceci +array << 6 #=> [1, 2, 3, 4, 5, 6] + +# Les Hash sont des dictionnaires Ruby contenant des paires de clé/valeur. +# Les Hash sont délimitées par des accolades : +hash = {'color' => 'green', 'number' => 5} + +hash.keys #=> ['color', 'number'] + +# Les Hash retournent la valeur +# en utilisant la clé associée à la valeur : +hash['color'] #=> 'green' +hash['number'] #=> 5 + +# Recherchez une clé inexistante dans une Hash retourne nil : +hash['nothing here'] #=> nil + +# Depuis Ruby 1.9, Une syntaxe spécifique est apparue en utilisant les symboles comme clés : + +new_hash = { defcon: 3, action: true} + +new_hash.keys #=> [:defcon, :action] + +# Astuce : Les tableaux et les Hash sont dénombrables +# Ils partagent un certain nombre de méthodes pratiques +# telle que each, map, count, etc... + +# Structures de contrôle + +if true + "si instruction" +elsif false + "autrement si, facultatif" +else + "autrement, également facultatif" +end + +for compteur in 1..5 + puts "itération #{compteur}" +end +#=> itération 1 +#=> itération 2 +#=> itération 3 +#=> itération 4 +#=> itération 5 + +# CEPENDANT, l'usage de la boucle for est très rare. +# À la place, utilisez la méthode "each" +# et passez lui un bloc de code. +# Un bloc de code est un ensemble d'instructions que vous pouvez passer à une methode comme "each". +# Les blocs sont similaires aux lambdas, les fonctions anonymes ou les closures dans d'autres langages. +# +# La méthode "each" exécute le bloc de code pour chaque élément de l'intervalle d'éléments. +# Le bloc de code passe un paramètre compteur. +# Appelez la méthode "each" avec un bloc de code comme ceci : + +(1..5).each do |compteur| + puts "itération #{compteur}" +end +#=> itération 1 +#=> itération 2 +#=> itération 3 +#=> itération 4 +#=> itération 5 + +# Vous pouvez également mettre un bloc de code entre accolades : +(1..5).each {|compteur| puts "itération #{compteur}"} + +# Le contenu des structures de données peut être parcouru +# en utilisant la méthode each. +array.each do |element| + puts "#{element} est une partie du tableau" +end +hash.each do |cle, valeur| + puts "#{cle} est #{valeur}" +end + +compteur = 1 +while compteur <= 5 do + puts "itération #{compteur}" + compteur += 1 +end +#=> itération 1 +#=> itération 2 +#=> itération 3 +#=> itération 4 +#=> itération 5 + +grade = 'B' + +case grade +when 'A' + puts "Excellent" +when 'B' + puts "Plus de chance la prochaine fois" +when 'C' + puts "Vous pouvez faire mieux" +when 'D' + puts "C'est pas terrible" +when 'F' + puts "Vous avez échoué!" +else + puts "Sytème de notation alternatif" +end + +# Fonctions + +def double(x) + x * 2 +end + +# Les fonctions (et tous les blocs de code) retournent +# implicitement la valeur de la dernière instruction évaluée +double(2) #=> 4 + +# Les paranthèses sont facultative +# lorsqu'il n'y a pas d'ambiguïté sur le résultat +double 3 #=> 6 + +double double 3 #=> 12 + +def sum(x,y) + x + y +end + +# Les paramètres de méthode sont séparés par des virgules +sum 3, 4 #=> 7 + +sum sum(3,4), 5 #=> 12 + +# yield +# Toutes les méthodes ont un argument facultatif et implicite +# de type bloc de code +# il peut être appelé avec le mot clé 'yield' + +def surround + puts "{" + yield + puts "}" +end + +surround { puts 'Bonjour tout le monde' } + +# { +# Bonjour tout le monde +# } + + +# Définissez une classe avec le mot clé 'class' +class Humain + + # Une variable de classe + # est partagée par toutes les instances de cette classe. + @@espece = "H. sapiens" + + # Constructeur de classe + def initialize(nom, age = 0) + # Affectez l'argument à la variable d'instance 'nom' + # pour la durée de vie de l'instance de cette classe + @nom = nom + # Si le paramètre 'age' est absent, + # la valeur par défaut définie dans la liste des arguments sera utilisée. + @age = age + end + + # Une simple méthode setter + def nom=(nom) + @nom = nom + end + + # Une simple méthode getter + def nom + @nom + end + + # Une méthode de classe utilise le mot clé 'self' + # pour se distinguer de la méthode d'instance. + # La méthode sera alors accessible à partir de la classe + # et non pas de l'instance. + def self.say(msg) + puts "#{msg}" + end + + def species + @@species + end + +end + + +# Instanciez une classe +jim = Humain.new("Jim Halpert") + +dwight = Humain.new("Dwight K. Schrute") + +# Appelez quelques méthodes +jim.espece #=> "H. sapiens" +jim.nom #=> "Jim Halpert" +jim.nom = "Jim Halpert II" #=> "Jim Halpert II" +jim.nom #=> "Jim Halpert II" +dwight.espece #=> "H. sapiens" +dwight.nom #=> "Dwight K. Schrute" + +# Appelez la méthode de classe +Humain.say("Hi") #=> "Hi" + +# Les classes sont également des objets en Ruby. +# Par conséquent, les classes peuvent avoir des variables d'instance. +# Les variables de classe sont partagées parmi +# la classe et ses descendants. + +# Classe parente +class Humain + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(valeur) + @@foo = valeur + end +end + +# Classe fille +class Travailleur < Humain +end + +Humain.foo # 0 +Travailleur.foo # 0 + +Humain.foo = 2 # 2 +Travailleur.foo # 2 + +# Les variables d'instance de classe ne sont pas partagées +# avec les classes héritées. + +class Humain + @bar = 0 + + def self.bar + @bar + end + + def self.bar=(valeur) + @bar = valeur + end +end + +class Docteur < Humain +end + +Humain.bar # 0 +Docteur.bar # nil + +``` diff --git a/java.html.markdown b/java.html.markdown index b4531635..cdcf620c 100644 --- a/java.html.markdown +++ b/java.html.markdown @@ -405,3 +405,7 @@ Other Topics To Research: * [Generics](http://docs.oracle.com/javase/tutorial/java/generics/index.html) * [Java Code Conventions](http://www.oracle.com/technetwork/java/codeconv-138413.html) + +Books: + +* [Head First Java] (http://www.headfirstlabs.com/books/hfjava/) diff --git a/objective-c.html.markdown b/objective-c.html.markdown index 2b1b3c67..b92e3218 100644 --- a/objective-c.html.markdown +++ b/objective-c.html.markdown @@ -160,7 +160,7 @@ int main (int argc, const char * argv[]) int jj; for (jj=0; jj < 4; jj++) { - NSLog(@"%d,", ii++); + NSLog(@"%d,", jj++); } // => prints "0," // "1," // "2," @@ -256,7 +256,7 @@ int main (int argc, const char * argv[]) } // Constructors are a way of creating classes -// This is a default constructor which is call when the object is creating +// This is a default constructor which is called when the object is creating - (id)init { if ((self = [super init])) diff --git a/pt-br/ruby-pt.html.markdown b/pt-br/ruby-pt.html.markdown new file mode 100644 index 00000000..8e8ce6a8 --- /dev/null +++ b/pt-br/ruby-pt.html.markdown @@ -0,0 +1,384 @@ +--- +language: ruby +filename: learnruby.rb +contributors: + - ["Bruno Henrique - Garu", "http://garulab.com"] + - ["Katyanna Moura", "https://twitter.com/amelie_kn"] +--- + +```ruby +# Isso é um comentário + +=begin +Isso é um comentário multilinha +Ninguém os usa +Você não deve usar também +=end + +# Primeiro e principal: Tudo é um objeto. + +# Números são objetos + +3.class #=> Fixnum + +3.to_s #=> "3" + + +# Um pouco de aritmética básica + +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 + +# Aritimética é apenas açúcar sintático +# para chamar um método de um objeto +1.+(3) #=> 4 +10.* 5 #=> 50 + +# Valores especiais são objetos +nil # Nada para ver aqui +true # verdadeiro +false # falso + +nil.class #=> NilClass +true.class #=> TrueClass +false.class #=> FalseClass + +# Igualdade +1 == 1 #=> true +2 == 1 #=> false + +# Desigualdade +1 != 1 #=> false +2 != 1 #=> true +!true #=> false +!false #=> true + +# além de 'false', 'nil' é o único outro valor falso + +!nil #=> true +!false #=> true +!0 #=> false + +# Mais comparações +1 < 10 #=> true +1 > 10 #=> false +2 <= 2 #=> true +2 >= 2 #=> true + +# Strings são objects + +'Eu sou uma string'.class #=> String +"Eu também sou uma string".class #=> String + +placeholder = "usar interpolação de string" +"Eu posso #{placeholder} quando estiver usando aspas duplas" +#=> "Eu posso usar insterpolação de string quando estiver usando aspas duplas" + +# imprime para output (saída) +puts "Estou imprimindo" + +# Variáveis +x = 25 #=> 25 +x #=> 25 + +# Note que uma atribuição retorna o valor atribuido +# Isso significa que você pode fazer múltiplas atribuições: + +x = y = 10 #=> 10 +x #=> 10 +y #=> 10 + +# Por convenção, use snake_case para nomes de variáveis +snake_case = true + +# Use nomes de variáveis descritivos +caminho_para_a_raiz_do_projeto = '/bom/nome/' +caminho = '/nome/ruim/' + +# Símbolos (são objetos) +# Símbolos são imutáveis, são constantes reutilizáveis representadadas internamente por um +# valor inteiro. Eles são frequentemente usados no lugar de strings para transmitir com eficiência os valores +# específicos e significativos + +:pendente.class #=> Symbol + +status = :pendente + +status == :pendente #=> true + +status == 'pendente' #=> false + +status == :aprovado #=> false + +# Arrays + +# Isso é um array +[1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] + +# Arrays podem conter diferentes tipos de itens + +array = [1, "Oi", false] #=> => [1, "Oi", false] + +# Arrays podem ser indexados +# a partir do começo +array[0] #=> 1 +array[12] #=> nil + +# Como aritimética, o acesso via [var] +# é apenas açúcar sintático +# para chamar o método [] de um objeto +array.[] 0 #=> 1 +array.[] 12 #=> nil + +# a partir do final +array[-1] #=> 5 + +# Com um índice de começo e fim +array[2, 4] #=> [3, 4, 5] + +# Ou com um intervalo de valores +array[1..3] #=> [2, 3, 4] + +# Adicionar a um array como este +array << 6 #=> [1, 2, 3, 4, 5, 6] + +# Hashes são o principal dicionário de Ruby com pares de chaves(keys)/valor(value). +# Hashes são simbolizados com chaves "{}" +hash = {'cor' => 'verde', 'numero' => 5} + +hash.keys #=> ['cor', 'numero'] + +# Hashes podem ser rapidamente pesquisados pela chave (key) +hash['cor'] #=> 'verde' +hash['numero'] #=> 5 + +# Procurar em um hash por uma chave que não existe retorna nil: +hash['nada aqui'] #=> nil + +# Interar sobre hashes com o método #each: +hash.each do |k, v| + puts "#{k} is #{v}" +end + +hash.each do |k, v| + puts "#{k} é #{v}" +end + +# Desde o Ruby 1.9, temos uma sintaxe especial quando usamos símbolos como chaves (keys) + +novo_hash = { defcon: 3, acao: true} + +novo_hash.keys #=> [:defcon, :acao] + +# Dica: Tanto Arrays quanto Hashes são Enumerable. +# Eles compartilham um monte de métodos úteis como each, map, count e mais + +# Estruturas de controle + +if true + "Se verdadeiro" +elsif false + "else if, opicional" +else + "else, também é opicional" +end + +for contador in 1..5 + puts "interação #{contador}" +end +#=> contador 1 +#=> contador 2 +#=> contador 3 +#=> contador 4 +#=> contador 5 + +# PORÉM +# Ninguém usa para loops +# Use "each" em vez, dessa forma: + +(1..5).each do |contador| + puts "interação #{contador}" +end +#=> contador 1 +#=> contador 2 +#=> contador 3 +#=> contador 4 +#=> contador 5 + +contador = 1 +while contador <= 5 do + puts "interação #{contador}" + contador += 1 +end +#=> contador 1 +#=> contador 2 +#=> contador 3 +#=> contador 4 +#=> contador 5 + +grau = 'B' + +case grau +when 'A' + puts "Um longo caminho a percorrer, pequeno gafanhoto" +when 'B' + puts "Melhor sorte da próxima vez" +when 'C' + puts "Você pode fazer melhor" +when 'D' + puts "Scraping through" +when 'F' + puts "Você falhou" +else + puts "Alternative grading system, eh?" +end + +# Funções + +def dobrar(x) + x * 2 +end + +# Funções (e todos os blocos) retornam implicitamente o valor da última linha +dobrar(2) #=> 4 + +# Parênteses são opicionais onde o resultado é claro +dobrar 3 #=> 6 + +dobrar dobrar 3 #=> 12 + +def somar(x,y) + x + y +end + +# Argumentos de métodos são separados por uma vírgula +somar 3, 4 #=> 7 + +somar somar(3,4), 5 #=> 12 + +# yield +# Todos os métodos possuem implicitamente um paramêntro opcional que é um bloco +# ele pode ser chamado com a palavra chave 'yield' + +def ao_redor + puts "{" + yield + puts "}" +end + +ao_redor { puts 'Olá mundo' } + +# { +# Olá mundo +# } + + +# Define uma classe com a palavra chave 'class' + +class Humano + + # Uma variável de classe. Ela é compartilhada por todas as instâncias dessa classe + @@especies = "H. sapiens" + + # Inicialização básica (contructor) + def initialize(nome, idade=0) + # Atribui o argumento para a variável de instancia "nome" do objeto + @nome = nome + # Se a idade não for passada, nós definimos um valor padrão na lista de argumentos + @idade = idade + end + + # Método básico para atribuir valor + def nome=(nome) + @nome = nome + end + + # Método básico de resgatar valor + def nome + @nome + end + + # Um método de classe usa a palavra chave self para se defenciar dos métodos de instância. + # Ele só pode ser chamado na classe, não na instancia + def self.diz(msg) + puts "#{msg}" + end + + def especies + @@especies + end + +end + + +# Instanciando uma classe +jim = Humano.new("Jim Halpert") + +dwight = Humano.new("Dwight K. Schrute") + +# Vamos chamar um par de métodos +jim.especies #=> "H. sapiens" + +jim.nome #=> "Jim Halpert" + +jim.nome = "Jim Halpert II" #=> "Jim Halpert II" + +jim.nome #=> "Jim Halpert II" + +dwight.especies #=> "H. sapiens" + +dwight.nome #=> "Dwight K. Schrute" + +# Chamar o método de classe +Humano.diz("Oi") #=> "Oi" + +# Uma classe também é objeto em Ruby. Então uma classe pode possuir variável de instância +# Variáveis de classe são compartilhadas entre a classe e todos os seus descendentes. + + +# Classe base +class Humano + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# classe filha +class Trabalhador < Humano +end + +Humano.foo # 0 +Trabalhador.foo # 0 + +Humano.foo = 2 # 2 +Trabalhador.foo # 2 + +# Uma variável de instância não é compartilhada por suas classes decendentes. + +class Humano + @bar = 0 + + def self.bar + @bar + end + + def self.bar=(value) + @bar = value + end +end + +class Doutor < Humano +end + +Humano.bar # 0 +Doutor.bar # nil + +``` diff --git a/ru-ru/clojure-ru.html.markdown b/ru-ru/clojure-ru.html.markdown new file mode 100644 index 00000000..48c16192 --- /dev/null +++ b/ru-ru/clojure-ru.html.markdown @@ -0,0 +1,426 @@ +--- +language: clojure +filename: learnclojure-ru.clj +contributors: + - ["Adam Bard", "http://adambard.com/"] + - ["Alexey Pirogov", "http://twitter.com/alex_pir"] + +--- + +Clojure, это представитель семейства Lisp-подобных языков, разработанный +для Java Virtual Machine. Язык идейно гораздо ближе к чистому +[функциональному программированию](https://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) чем его прародитель Common Lisp, но в то же время обладает набором инструментов для работы с состоянием, +таких как [STM](https://ru.wikipedia.org/wiki/Software_transactional_memory). + +Благодаря такому сочетанию технологий в одном языке, разработка программ, +предполагающих конкурентное выполнение, значительно упрощается +и даже может быть автоматизирована. + +(Последующие примеры кода предполагают выполнение в Clojure версии 1.2 и выше) + + +```clojure +; Комментарии начинаются символом ";". + +; Код на языке Clojure записывается в виде "форм", +; которые представляют собой обычные списки элементов, разделенных пробелами, +; заключённые в круглые скобки +; +; Clojure Reader (инструмент языка, отвечающий за чтение исходного кода), +; анализируя форму, предполагает, что первым элементом формы (т.е. списка) +; является функция или макрос, который следует вызвать, передав ему +; в качестве аргументов остальные элементы списка-формы. + +; Первым вызовом в файле должен быть вызов функции ns, +; которая отвечает за выбор текущего пространства имен (namespace) +(ns learnclojure-ru) + +; Несколько простых примеров: + +; str объединяет в единую строку все свои аргументы +(str "Hello" " " "World") ; => "Hello World" + +; Арифметика тоже выглядит несложно +(+ 1 1) ; => 2 +(- 2 1) ; => 1 +(* 1 2) ; => 2 +(/ 2 1) ; => 2 + +; Проверка на равенство (Equality) +(= 1 1) ; => true +(= 2 1) ; => false + +; Для булевой логики вам может понадобиться not +(not true) ; => false + +; Вложенные формы, конечно же, допустимы и работают вполне предсказуемо +(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2 + +; Типы +;;;;;;;;;;;;; + +; Clojure использует типы Java для представления булевых значений, +; строк и чисел +; Узнать тип мы можем, использую функцию `class +(class 1) ; Целочисленные литералы типа по-умолчанию являются java.lang.Long +(class 1.) ; Числа с плавающей точкой, это java.lang.Double +(class "") ; Строки всегда заключаются в двойные кавычки + ; и представляют собой java.lang.String +(class false) ; Булевы значения, это экземпляры java.lang.Boolean +(class nil) ; "Пустое" значение называется "nil" + +; Если Вы захотите создать список из чисел, вы можете просто +; предварить форму списка символом "'", который подскажет Reader`у, +; что эта форма не требует вычисления +'(+ 1 2) ; => (+ 1 2) +; ("'", это краткая запись формы (quote (+ 1 2)) + +; "Квотированный" список можно вычислить, передав его функции eval +(eval '(+ 1 2)) ; => 3 + +; Коллекции и Последовательности +;;;;;;;;;;;;;;;;;;; + +; Списки (Lists) в clojure структурно представляют собой "связанные списки", +; тогда как Векторы (Vectors), устроены как массивы. +; Векторы и Списки тоже являются классами Java! +(class [1 2 3]); => clojure.lang.PersistentVector +(class '(1 2 3)); => clojure.lang.PersistentList + +; Список может быть записан, как (1 2 3), но в этом случае +; он будет воспринят reader`ом, как вызов функции. +; Есть два способа этого избежать: +; '(1 2 3) - квотирование, +; (list 1 2 3) - явное конструирование списка с помощью функции list. + +; "Коллекции", это некие наборы данных +; И списки, и векторы являются коллекциями: +(coll? '(1 2 3)) ; => true +(coll? [1 2 3]) ; => true + +; "Последовательности" (seqs), это абстракция над наборами данных, +; элементы которых "упакованы" последовательно. +; Списки - последовательности, а вектора - нет. +(seq? '(1 2 3)) ; => true +(seq? [1 2 3]) ; => false + +; Любая seq предоставляет доступ только к началу последовательности данных, +; не предоставляя информацию о её длине. +; При этом последовательности могут быть и бесконечными, +; т.к. являются ленивыми и предоставляют данные только по требованию! +(range 4) ; => (0 1 2 3) +(range) ; => (0 1 2 3 4 ...) (бесконечная последовательность!) +(take 4 (range)) ; (0 1 2 3) + +; Добавить элемент в начало списка или вектора можно с помощью функции cons +(cons 4 [1 2 3]) ; => (4 1 2 3) +(cons 4 '(1 2 3)) ; => (4 1 2 3) + +; Функция conj добавляет элемент в коллекцию +; максимально эффективным для неё способом. +; Для списков эффективно добавление в начло, а для векторов - в конец. +(conj [1 2 3] 4) ; => [1 2 3 4] +(conj '(1 2 3) 4) ; => (4 1 2 3) + +; Функция concat объединяет несколько списков и векторов в единый список +(concat [1 2] '(3 4)) ; => (1 2 3 4) + +; Работать с коллекциями удобно с помощью функций filter и map +(map inc [1 2 3]) ; => (2 3 4) +(filter even? [1 2 3]) ; => (2) + +; reduce поможет "свернуть" коллекцию +(reduce + [1 2 3 4]) +; = (+ (+ (+ 1 2) 3) 4) +; => 10 + +; Вызывая reduce, мы можем указать начальное значение +(reduce conj [] '(3 2 1)) +; = (conj (conj (conj [] 3) 2) 1) +; => [3 2 1] + +; Функции +;;;;;;;;;;;;;;;;;;;;; + +; Функция создается специальной формой fn. +; "Тело"" функции может состоять из нескольких форм, +; но результатом вызова функции всегда будет результат вычисления +; последней из них. +(fn [] "Hello World") ; => fn + +; (Вызов функции требует "оборачивания" fn-формы в форму вызова) +((fn [] "Hello World")) ; => "Hello World" + +; Назначить значению имя можно специальной формой def +(def x 1) +x ; => 1 + +; Назначить имя можно любому значению, в т.ч. и функции: +(def hello-world (fn [] "Hello World")) +(hello-world) ; => "Hello World" + +; Поскольку именование функций - очень частая операция, +; clojure позволяет, сделать это проще: +(defn hello-world [] "Hello World") + +; Вектор [] в форме описания функции, следующий сразу за именем, +; описывает параметры функции: +(defn hello [name] + (str "Hello " name)) +(hello "Steve") ; => "Hello Steve" + +; Одна функция может иметь сразу несколько наборов аргументов: +(defn hello3 + ([] "Hello World") + ([name] (str "Hello " name))) +(hello3 "Jake") ; => "Hello Jake" +(hello3) ; => "Hello World" + +; Также функция может иметь набор аргументов переменной длины +(defn count-args [& args] ; args будет содержать seq аргументов + (str "You passed " (count args) " args: " args)) +(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)" + +; Можно комбинировать оба подхода задания аргументов +(defn hello-count [name & args] + (str "Hello " name ", you passed " (count args) " extra args")) +(hello-count "Finn" 1 2 3) +; => "Hello Finn, you passed 3 extra args" + +; Для создания анонимных функций есть специальный синтаксис: +; функциональные литералы +(def hello2 #(str "Hello " %1)) +(hello2 "Fanny") ; => "Hello Fanny" + +; такие функциональные литералы удобно использовать с map, filter и reduce +(map #(* 10 %1) [1 2 3 5]) ; => (10 20 30 50) +(filter #(> %1 3) [1 2 3 4 5 6 7]) ; => (4 5 6 7) +(reduce #(str %1 "," %2) [1 2 3 4]) ; => "1,2,3,4" + +; Отображения (Maps) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Hash maps и array maps имеют одинаковый интерфейс. +; Hash maps производят поиск по ключу быстрее, но не сохраняют порядок ключей +(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap +(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap + +; Array maps автоматически преобразуются в hash maps, +; как только разрастутся до определенного размера + +; Отображения могут использовать в качестве ключей любые хэшируемые значения, +; однако предпочтительными являются ключи, +; являющиеся "ключевыми словами" (keywords) +(class :a) ; => clojure.lang.Keyword + +(def stringmap {"a" 1, "b" 2, "c" 3}) +stringmap ; => {"a" 1, "b" 2, "c" 3} + +(def keymap {:a 1, :b 2, :c 3}) +keymap ; => {:a 1, :c 3, :b 2} + +; Предыдущий пример содержит запятые в коде, однако reader не использует их, +; при обработке литералов - запятые просто воспринимаются, +; как "пробельные символы" (whitespaces) + +; Отображение может выступать в роли функции, возвращающей значение по ключу +(stringmap "a") ; => 1 +(keymap :a) ; => 1 + +; При попытке получить отсутствующее значение, будет возвращён nil +(stringmap "d") ; => nil + +; Иногда бывает удобно указать конкретное значение по-умолчанию: +({:a 1 :b 2} :c "Oops!") ; => "Oops!" + +; Keywords тоже могут использоваться в роли функций! +(:b keymap) ; => 2 + +; Однако этот фокус не пройдёт со строками. +;("a" stringmap) +; => Exception: java.lang.String cannot be cast to clojure.lang.IFn + +; Добавить пару ключ-значение в отображение можно функцией assoc +(def newkeymap (assoc keymap :d 4)) +newkeymap ; => {:a 1, :b 2, :c 3, :d 4} + +; Но всегда следует помнить, что значения в Clojure - неизменяемые! +keymap ; => {:a 1, :b 2, :c 3} - оригинал не был затронут + +; dissoc позволяет исключить значение по ключу +(dissoc keymap :a :b) ; => {:c 3} + +; Множества (Sets) +;;;;;;;;;;;;;;;;;; + +(class #{1 2 3}) ; => clojure.lang.PersistentHashSet +(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3} + +; Добавляются элементы посредством conj +(conj #{1 2 3} 4) ; => #{1 2 3 4} + +; Исключаются - посредством disj +(disj #{1 2 3} 1) ; => #{2 3} + +; Вызов множества, как функции, позволяет проверить +; принадлежность элемента этому множеству: +(#{1 2 3} 1) ; => 1 +(#{1 2 3} 4) ; => nil + +; В пространстве имен clojure.sets +; содержится множество функций для работы с множествами + +; Полезные формы +;;;;;;;;;;;;;;;;; + +; Конструкции ветвления в clojure, это обычные макросы +; и подобны их собратьям в других языках: +(if false "a" "b") ; => "b" +(if false "a") ; => nil + +; Специальная форма let позволяет присвоить имена значениям локально. +; При этом все изменения будут видны только вложенным формам: +(def a 10) +(let [a 1 b 2] + (> a b)) ; => false + +; Несколько форм можно объединить в одну форму посредством do +; Значением do-формы будет значение последней формы из списка вложенных в неё: +(do + (print "Hello") + "World") ; => "World" (prints "Hello") + +; Множество макросов содержит внутри себя неявную do-форму. +; Пример - макрос определения функции: +(defn print-and-say-hello [name] + (print "Saying hello to " name) + (str "Hello " name)) +(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff") + +; Ещё один пример - let: +(let [name "Urkel"] + (print "Saying hello to " name) + (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel") + +; Модули +;;;;;;;;; + +; Форма "use" позволяет добавить в текущее пространство имен +; все имена (вместе со значениями) из указанного модуля: +(use 'clojure.set) + +; Теперь нам доступны операции над множествами: +(intersection #{1 2 3} #{2 3 4}) ; => #{2 3} +(difference #{1 2 3} #{2 3 4}) ; => #{1} + +; use позволяет указать, какие конкретно имена +; должны быть импортированы из модуля: +(use '[clojure.set :only [intersection]]) + +; Также модуль может быть импортирован формой require +(require 'clojure.string) + +; После этого модуль становится доступе в текущем пространстве имен, +; а вызов его функций может быть осуществлен указанием полного имени функции: +(clojure.string/blank? "") ; => true + +; Импортируемому модулю можно назначить короткое имя: +(require '[clojure.string :as str]) +(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst." +; (Литерал вида #"" обозначает регулярное выражение) + +; Вместо отдельной формы require (и use, хотя это и не приветствуется) можно +; указать необходимые модули прямо в форме ns: +(ns test + (:require + [clojure.string :as str] ; Внимание: при указании внутри формы ns + [clojure.set :as set])) ; имена пакетов не квотируются! + +; Java +;;;;;;; + +; Стандартная библиотека Java очень богата, +; и всё это богатство доступно и для Clojure! + +; import позволяет импортировать модули Java +(import java.util.Date) + +; В том числе и из ns +(ns test + (:import java.util.Date + java.util.Calendar)) + +; Имя класса, сопровождаемое символом "." позволяет +; инстанцировать объекты Java-классов: +(Date.) ; <a date object> + +; форма . позволяет вызывать методы: +(. (Date.) getTime) ; <a timestamp> +(.getTime (Date.)) ; а можно и так + +; Статические методы вызываются как функции модуля: +(System/currentTimeMillis) ; <a timestamp> (Модуль system всегда доступен!) + +; doto позволяет удобно работать с объектами, изменяющими свое состояние +(import java.util.Calendar) +(doto (Calendar/getInstance) + (.set 2000 1 1 0 0 0) + .getTime) ; => A Date. set to 2000-01-01 00:00:00 + +; Работа с изменяемым сотоянием +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Clojure предоставляет набор инструментов +; для работы с изменяемым состоянием: Software Transactional Memory. +; Структуры STM представлены тремя типами: +; - атомы (atoms) +; - агенты (agents) +; - ссылки (references) + +; Самые простые хранители состояния - атомы: +(def my-atom (atom {})) ; {} - начальное состояние атома + +; Обновляется атом посредством swap!. +; swap! применяет функцию аргумент к текущему значению +; атома и помещает в атом результат +(swap! my-atom assoc :a 1) ; Обновляет my-atom, помещая в него (assoc {} :a 1) +(swap! my-atom assoc :b 2) ; Обновляет my-atom, помещая в него (assoc {:a 1} :b 2) + +; Получить значение атома можно посредством '@' +; (провести так называемую операцию dereference) +my-atom ;=> Atom<#...> (Возвращает объект типа Atom) +@my-atom ; => {:a 1 :b 2} + +; Пример реализации счётчика на атоме +(def counter (atom 0)) +(defn inc-counter [] + (swap! counter inc)) + +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) + +@counter ; => 5 + +; С другими STM-конструкциями - refs и agents - можно ознакомиться тут: +; Refs: http://clojure.org/refs +; Agents: http://clojure.org/agents +``` + +### Для будущего чтения + +Это руководство не претендует на полноту, но мы смеем надеяться, способно вызвать интерес к дальнейшему изучению языка. + +Clojure.org - сайт содержит большое количество статей по языку: +[http://clojure.org/](http://clojure.org/) + +Clojuredocs.org - сайт документации языка с примерами использования функций: +[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core) + +4Clojure - отличный способ закрепить навыки программирования на clojure, решая задачи вместе с коллегами со всего мира: +[http://www.4clojure.com/](http://www.4clojure.com/) + +Clojure-doc.org (да, именно) неплохой перечень статей для начинающих: +[http://clojure-doc.org/](http://clojure-doc.org/) diff --git a/ruby-ecosystem.html.markdown b/ruby-ecosystem.html.markdown index cae55cd3..54c1d178 100644 --- a/ruby-ecosystem.html.markdown +++ b/ruby-ecosystem.html.markdown @@ -81,7 +81,7 @@ development. Less mature/compatible: * Topaz - Written in RPython (using the PyPy toolchain), Topaz is fairly young - and not yet compatable. It shows promise to be a high-performance ruby + and not yet compatible. It shows promise to be a high-performance ruby implementation. * IronRuby - Written in C# targeting the .NET platform, work on IronRuby seems to have stopped since Microsoft pulled their support. diff --git a/ruby.html.markdown b/ruby.html.markdown index 19f2ec86..3a233d98 100644 --- a/ruby.html.markdown +++ b/ruby.html.markdown @@ -36,7 +36,7 @@ You shouldn't either # Arithmetic is just syntactic sugar # for calling a method on an object 1.+(3) #=> 4 -10.* 5 #=> 50 +10.* 5 #=> 50 # Special values are objects nil # Nothing to see here @@ -242,7 +242,7 @@ when 'D' puts "Scraping through" when 'F' puts "You failed!" -else +else puts "Alternative grading system, eh?" end @@ -252,7 +252,7 @@ def double(x) x * 2 end -# Functions (and all blocks) implcitly return the value of the last statement +# Functions (and all blocks) implicitly return the value of the last statement double(2) #=> 4 # Parentheses are optional where the result is unambiguous diff --git a/zh-cn/go-zh.html.markdown b/zh-cn/go-zh.html.markdown new file mode 100644 index 00000000..25fd1f03 --- /dev/null +++ b/zh-cn/go-zh.html.markdown @@ -0,0 +1,279 @@ +--- +名字:Go +分类:编程语言 +文件名:learngo.go +贡献者: + - ["Sonia Keys", "https://github.com/soniakeys"] + - ["pantaovay", "https://github.com/pantaovay"] +--- + +发明Go语言是出于更好地完成工作的需要。Go不是计算机科学的最新发展潮流,但它却提供了解决现实问题的最新最快的方法。 + +Go拥有命令式语言的静态类型,编译很快,执行也很快,同时加入了对于目前多核CPU的并发计算支持,也有相应的特性来实现大规模编程。 + +Go语言有非常棒的标准库,还有一个充满热情的社区。 + +```Go +// 单行注释 +/* 多行 + 注释 */ + +// 导入包的子句在每个源文件的开头。 +// Main比较特殊,它用来声明可执行文件,而不是一个库。 +package main + +// Import语句声明了当前文件引用的包。 +import ( + "fmt" // Go语言标准库中的包 + "net/http" // 一个web服务器包 + "strconv" // 字符串转换 +) + +//函数声明:Main是程序执行的入口。不管你喜欢还是不喜欢,反正G就用了花括号来包住函数体。 +func main() { + // 往标准输出打印一行。 + // 用包名fmt限制打印函数。 + fmt.Println("Hello world!") + + // 调用当前包的另一个函数。 + beyondHello() +} + +// 函数可以在括号里加参数。 +// 如果没有参数的话,也需要一个空括号。 +func beyondHello() { + var x int // 变量声明,变量必须在使用之前声明。 + x = 3 // 变量赋值。 + // 可以用:=来偷懒,它自动把变量类型、声明和赋值都搞定了。 + y := 4 + sum, prod := learnMultiple(x, y) // 多个返回变量的函数 + fmt.Println("sum:", sum, "prod:", prod) // 简单输出 + learnTypes() // 少于y分钟,学的更多! +} + +// 多变量和多返回值的函数 +func learnMultiple(x, y int) (sum, prod int) { + return x + y, x * y // 返回两个值 +} + +// 内置变量类型和关键词 +func learnTypes() { + // 短声明给你所想。 + s := "Learn Go!" // String类型 + + s2 := `A "raw" string literal +can include line breaks.` // 同样是String类型 + + // 非ascii字符。Go使用UTF-8编码。 + g := 'Σ' // rune类型,uint32的别名,使用UTF-8编码 + + f := 3.14195 // float64类型,IEEE-754 64位浮点数 + c := 3 + 4i // complex128类型,内部使用两个float64表示 + + // Var变量可以直接初始化。 + var u uint = 7 // unsigned 无符号变量,但是实现依赖int型变量的长度 + var pi float32 = 22. / 7 + + // 字符转换 + n := byte('\n') // byte是uint8的别名 + + // 数组类型编译的时候大小固定。 + var a4 [4] int // 有4个int变量的数组,初始为0 + a3 := [...]int{3, 1, 5} // 有3个int变量的数组,同时进行了初始化 + + // Slice 有动态大小。Array和Slice各有千秋,但是使用slice的地方更多些。 + s3 := []int{4, 5, 9} // 和a3相比,这里没有省略号 + s4 := make([]int, 4) // 分配一个有4个int型变量的slice,全部被初始化为0 + + var d2 [][]float64 // 声明而已,什么都没有分配 + bs := []byte("a slice") // 类型转换的语法 + + p, q := learnMemory() // 声明p,q为int型变量的指针 + fmt.Println(*p, *q) // * 取值 + + // Map是动态可增长关联数组,和其他语言中的hash或者字典相似。 + m := map[string]int{"three": 3, "four": 4} + m["one"] = 1 + + // 在Go语言中未使用的变量在编译的时候会报错,而不是warning。 + // 下划线 _ 可以使你“使用”一个变量,但是丢弃它的值。 + _,_,_,_,_,_,_,_,_ = s2, g, f, u, pi, n, a3, s4, bs + // 输出变量 + fmt.Println(s, c, a4, s3, d2, m) + + learnFlowControl() // 回到流程控制 +} + +// Go全面支持垃圾回收。Go有指针,但是不支持指针运算。 +// 你会因为空指针而犯错,但是不会因为增加指针而犯错。 +func learnMemory() (p, q *int) { + // 返回int型变量指针p和q + p = new(int) // 内置函数new分配内存 + // 自动将分配的int赋值0,p不再是空的了。 + s := make([]int, 20) // 给20个int变量分配一块内存 + s[3] = 7 // 赋值 + r := -2 // 声明另一个局部变量 + return &s[3], &r // & 取址 +} + +func expensiveComputation() int { + return 1e6 +} + +func learnFlowControl() { + // If需要花括号,括号就免了 + if true { + fmt.Println("told ya") + } + // 用go fmt 命令可以帮你格式化代码,所以不用怕被人吐槽代码风格了,也不用容忍被人的代码风格。 + if false { + // pout + } else { + // gloat + } + // 如果太多嵌套的if语句,推荐使用switch + x := 1 + switch x { + case 0: + case 1: + // 隐式调用break语句,匹配上一个即停止 + case 2: + // 不会运行 + } + // 和if一样,for也不用括号 + for x := 0; x < 3; x++ { // ++ 自增 + fmt.Println("iteration", x) + } + // x在这里还是1。为什么? + + // for 是go里唯一的循环关键字,不过它有很多变种 + for { // 无限循环 + break // 骗你的 + continue // 不会运行的 + } + // 和for一样,if中的:=先给y赋值,然后再和x作比较。 + if y := expensiveComputation(); y > x { + x = y + } + // 闭包函数 + xBig := func() bool { + return x > 100 // x是上面声明的变量引用 + } + fmt.Println("xBig:", xBig()) // true (上面把y赋给x了) + x /= 1e5 // x变成10 + fmt.Println("xBig:", xBig()) // 现在是false + + // 当你需要goto的时候,你会爱死它的! + goto love +love: + + learnInterfaces() // 好东西来了! +} + +// 定义Stringer为一个接口类型,有一个方法String +type Stringer interface { + String() string +} + +// 定义pair为一个结构体,有x和y两个int型变量。 +type pair struct { + x, y int +} + +// 定义pair类型的方法,实现Stringer接口。 +func (p pair) String() string { // p被叫做“接收器” + // Sprintf是fmt包中的另一个公有函数。 + // 用 . 调用p中的元素。 + return fmt.Sprintf("(%d, %d)", p.x, p.y) +} + +func learnInterfaces() { + // 花括号用来定义结构体变量,:=在这里将一个结构体变量赋值给p。 + p := pair{3, 4} + fmt.Println(p.String()) // 调用pair类型p的String方法 + var i Stringer // 声明i为Stringer接口类型 + i = p // 有效!因为p实现了Stringer接口(类似java中的塑型) + // 调用i的String方法,输出和上面一样 + fmt.Println(i.String()) + + // fmt包中的Println函数向对象要它们的string输出,实现了String方法就可以这样使用了。(类似java中的序列化) + fmt.Println(p) // 输出和上面一样,自动调用String函数。 + fmt.Println(i) // 输出和上面一样。 + + learnErrorHandling() +} + +func learnErrorHandling() { + // ", ok"用来判断有没有正常工作 + m := map[int]string{3: "three", 4: "four"} + if x, ok := m[1]; !ok { // ok 为false,因为m中没有1 + fmt.Println("no one there") + } else { + fmt.Print(x) // 如果x在map中的话,x就是那个值喽。 + } + // 错误可不只是ok,它还可以给出关于问题的更多细节。 + if _, err := strconv.Atoi("non-int"); err != nil { // _ discards value + // 输出"strconv.ParseInt: parsing "non-int": invalid syntax" + fmt.Println(err) + } + // 待会再说接口吧。同时, + learnConcurrency() +} + +// c是channel类型,一个并发安全的通信对象。 +func inc(i int, c chan int) { + c <- i + 1 // <-把右边的发送到左边的channel。 +} + +// 我们将用inc函数来并发地增加一些数字。 +func learnConcurrency() { + // 用make来声明一个slice,make会分配和初始化slice,map和channel。 + c := make(chan int) + // 用go关键字开始三个并发的goroutine,如果机器支持的话,还可能是并行执行。三个都被发送到同一个channel。 + go inc(0, c) // go is a statement that starts a new goroutine. + go inc(10, c) + go inc(-805, c) + // 从channel中独处结果并打印。 + // 打印出什么东西是不可预知的。 + fmt.Println(<-c, <-c, <-c) // channel在右边的时候,<-是接收操作。 + + cs := make(chan string) // 操作string的channel + cc := make(chan chan string) // 操作channel的channel + go func() { c <- 84 }() // 开始一个goroutine来发送一个新的数字 + go func() { cs <- "wordy" }() // 发送给cs + // Select类似于switch,但是每个case包括一个channel操作。它随机选择一个准备好通讯的case。 + select { + case i := <-c: // 从channel接收的值可以赋给其他变量 + fmt.Println("it's a", i) + case <-cs: // 或者直接丢弃 + fmt.Println("it's a string") + case <-cc: // 空的,还没作好通讯的准备 + fmt.Println("didn't happen.") + } + // 上面c或者cs的值被取到,其中一个goroutine结束,另外一个保持阻塞。 + + learnWebProgramming() // Go很适合web编程,我知道你也想学! +} + +// http包中的一个简单的函数就可以开启web服务器。 +func learnWebProgramming() { + // ListenAndServe第一个参数指定了监听端口,第二个参数是一个接口,特定是http.Handler。 + err := http.ListenAndServe(":8080", pair{}) + fmt.Println(err) // 不要无视错误。 +} + +// 使pair实现http.Handler接口的ServeHTTP方法。 +func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // 使用http.ResponseWriter返回数据 + w.Write([]byte("You learned Go in Y minutes!")) +} +``` + +## 更进一步 + +Go的根源在[Go官方网站](http://golang.org/)。 +在那里你可以学习入门教程,通过浏览器交互式地学习,而且可以读到很多东西。 + +强烈推荐阅读语言定义部分,很简单而且很简洁!(as language definitions go these days.) + +学习Go还要阅读Go标准库的源代码,全部文档化了,可读性非常好,可以学到go,go style和go idioms。在文档中点击函数名,源代码就出来了! |