mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 09:41:36 +00:00
[crystal/en] It would be greate to add crystal language :) (#2600)
* Add Crystal language * Add Type informations and fix issues * Add missing examples * Add name in contributor list * Update code according to feedback received
This commit is contained in:
parent
db8cc8bd78
commit
919e44f843
553
crystal.html.markdown
Normal file
553
crystal.html.markdown
Normal file
@ -0,0 +1,553 @@
|
||||
---
|
||||
language: crystal
|
||||
filename: learncrystal.cr
|
||||
contributors:
|
||||
- ["Vitalii Elenhaupt", "http://veelenga.com"]
|
||||
- ["Arnaud Fernandés", "https://github.com/TechMagister/"]
|
||||
|
||||
---
|
||||
|
||||
```crystal
|
||||
|
||||
# This is a comment
|
||||
|
||||
# Everything is an object
|
||||
nil.class #=> Nil
|
||||
100.class #=> Int32
|
||||
true.class #=> Bool
|
||||
|
||||
# Falsey values are: nil, false and null pointers
|
||||
!nil #=> true : Bool
|
||||
!false #=> true : Bool
|
||||
!0 #=> false : Bool
|
||||
|
||||
# Integers
|
||||
|
||||
1.class #=> Int32
|
||||
|
||||
# Four signed integer types
|
||||
1_i8.class #=> Int8
|
||||
1_i16.class #=> Int16
|
||||
1_i32.class #=> Int32
|
||||
1_i64.class #=> Int64
|
||||
|
||||
# Four unsigned integer types
|
||||
1_u8.class #=> UInt8
|
||||
1_u16.class #=> UInt16
|
||||
1_u32.class #=> UInt32
|
||||
1_u64.class #=> UInt64
|
||||
|
||||
2147483648.class #=> Int64
|
||||
9223372036854775808.class #=> UInt64
|
||||
|
||||
# Binary numbers
|
||||
0b1101 #=> 13 : Int32
|
||||
|
||||
# Octal numbers
|
||||
0o123 #=> 83 : Int32
|
||||
|
||||
# Hexadecimal numbers
|
||||
0xFE012D #=> 16646445 : Int32
|
||||
0xfe012d #=> 16646445 : Int32
|
||||
|
||||
# Floats
|
||||
|
||||
1.0.class #=> Float64
|
||||
|
||||
# There are two floating point types
|
||||
1.0_f32.class #=> Float32
|
||||
1_f32.class #=> Float32
|
||||
|
||||
1e10.class #=> Float64
|
||||
1.5e10.class #=> Float64
|
||||
1.5e-7.class #=> Float64
|
||||
|
||||
# Chars
|
||||
|
||||
'a'.class #=> Char
|
||||
|
||||
# Octal codepoint
|
||||
'\101' #=> 'A' : Char
|
||||
|
||||
# Unicode codepoint
|
||||
'\u0041' #=> 'A' : Char
|
||||
|
||||
# Strings
|
||||
|
||||
"s".class #=> String
|
||||
|
||||
# Strings are immutable
|
||||
s = "hello, " #=> "hello, " : String
|
||||
s.object_id #=> 134667712 : UInt64
|
||||
s += "Crystal" #=> "hello, Crystal" : String
|
||||
s.object_id #=> 142528472 : UInt64
|
||||
|
||||
# Supports interpolation
|
||||
"sum = #{1 + 2}" #=> "sum = 3" : String
|
||||
|
||||
# Multiline string
|
||||
"This is
|
||||
multiline string"
|
||||
|
||||
# String with double quotes
|
||||
%(hello "world") #=> "hello \"world\""
|
||||
|
||||
# Symbols
|
||||
# Immutable, reusable constants represented internally as Int32 integer value.
|
||||
# They're often used instead of strings to efficiently convey specific,
|
||||
# meaningful values
|
||||
|
||||
:symbol.class #=> Symbol
|
||||
|
||||
sentence = :question? # :"question?" : Symbol
|
||||
|
||||
sentence == :question? #=> true : Bool
|
||||
sentence == :exclamation! #=> false : Bool
|
||||
sentence == "question?" #=> false : Bool
|
||||
|
||||
# Arrays
|
||||
|
||||
[1, 2, 3].class #=> Array(Int32)
|
||||
[1, "hello", 'x'].class #=> Array(Int32 | String | Char)
|
||||
|
||||
# Empty arrays should define a type
|
||||
[] # Syntax error: for empty arrays use '[] of ElementType'
|
||||
[] of Int32 #=> [] : Array(Int32)
|
||||
Array(Int32).new #=> [] : Array(Int32)
|
||||
|
||||
# Arrays can be indexed
|
||||
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32)
|
||||
array[0] #=> 1 : Int32
|
||||
array[10] # raises IndexError
|
||||
array[-6] # raises IndexError
|
||||
array[10]? #=> nil : (Int32 | Nil)
|
||||
array[-6]? #=> nil : (Int32 | Nil)
|
||||
|
||||
# From the end
|
||||
array[-1] #=> 5
|
||||
|
||||
# With a start index and size
|
||||
array[2, 3] #=> [3, 4, 5]
|
||||
|
||||
# Or with range
|
||||
array[1..3] #=> [2, 3, 4]
|
||||
|
||||
# Add to an array
|
||||
array << 6 #=> [1, 2, 3, 4, 5, 6]
|
||||
|
||||
# Remove from the end of the array
|
||||
array.pop #=> 6
|
||||
array #=> [1, 2, 3, 4, 5]
|
||||
|
||||
# Remove from the beginning of the array
|
||||
array.shift #=> 1
|
||||
array #=> [2, 3, 4, 5]
|
||||
|
||||
# Check if an item exists in an array
|
||||
array.includes? 3 #=> true
|
||||
|
||||
# Special syntax for an array of string and an array of symbols
|
||||
%w(one two three) #=> ["one", "two", "three"] : Array(String)
|
||||
%i(one two three) #=> [:one, :two, :three] : Array(Symbol)
|
||||
|
||||
# There is a special array syntax with other types too, as long as
|
||||
# they define a .new and a #<< method
|
||||
set = Set{1, 2, 3} #=> [1, 2, 3]
|
||||
set.class #=> Set(Int32)
|
||||
|
||||
# The above is equivalent to
|
||||
set = Set(typeof(1, 2, 3)).new
|
||||
set << 1
|
||||
set << 2
|
||||
set << 3
|
||||
|
||||
# Hashes
|
||||
|
||||
{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32)
|
||||
{1 => 2, 'a' => 3}.class #=> Hash(Int32 | Char, Int32)
|
||||
|
||||
# Empty hashes should specify a type
|
||||
{} # Syntax error
|
||||
{} of Int32 => Int32 # {}
|
||||
Hash(Int32, Int32).new # {}
|
||||
|
||||
# Hashes can be quickly looked up by key
|
||||
hash = {"color" => "green", "number" => 5}
|
||||
hash["color"] #=> "green"
|
||||
hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError)
|
||||
hash["no_such_key"]? #=> nil
|
||||
|
||||
# Check existence of keys hash
|
||||
hash.has_key? "color" #=> true
|
||||
|
||||
# Special notation for symbol and string keys
|
||||
{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'}
|
||||
{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'}
|
||||
|
||||
# Special hash literal syntax with other types too, as long as
|
||||
# they define a .new and a #[]= methods
|
||||
class MyType
|
||||
def []=(key, value)
|
||||
puts "do stuff"
|
||||
end
|
||||
end
|
||||
|
||||
MyType{"foo" => "bar"}
|
||||
|
||||
# The above is equivalent to
|
||||
tmp = MyType.new
|
||||
tmp["foo"] = "bar"
|
||||
tmp
|
||||
|
||||
# Ranges
|
||||
|
||||
1..10 #=> Range(Int32, Int32)
|
||||
Range.new(1, 10).class #=> Range(Int32, Int32)
|
||||
|
||||
# Can be inclusive or exclusive
|
||||
(3..5).to_a #=> [3, 4, 5]
|
||||
(3...5).to_a #=> [3, 4]
|
||||
|
||||
# Check whether range includes the given value or not
|
||||
(1..8).includes? 2 #=> true
|
||||
|
||||
# Tuples are a fixed-size, immutable, stack-allocated sequence of values of
|
||||
# possibly different types.
|
||||
{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char)
|
||||
|
||||
# Acces tuple's value by its index
|
||||
tuple = {:key1, :key2}
|
||||
tuple[1] #=> :key2
|
||||
tuple[2] #=> syntax error : Index out of bound
|
||||
|
||||
# Can be expanded into multiple variables
|
||||
a, b, c = {:a, 'b', "c"}
|
||||
a #=> :a
|
||||
b #=> 'b'
|
||||
c #=> "c"
|
||||
|
||||
# Procs represent a function pointer with an optional context (the closure data)
|
||||
# It is typically created with a proc litteral
|
||||
proc = ->(x : Int32) { x.to_s }
|
||||
proc.class # Proc(Int32, String)
|
||||
# Or using the new method
|
||||
Proc(Int32, String).new { |x| x.to_s }
|
||||
|
||||
# Invoke proc with call method
|
||||
proc.call 10 #=> "10"
|
||||
|
||||
# Control statements
|
||||
|
||||
if true
|
||||
"if statement"
|
||||
elsif false
|
||||
"else-if, optional"
|
||||
else
|
||||
"else, also optional"
|
||||
end
|
||||
|
||||
puts "if as a suffix" if true
|
||||
|
||||
# If as an expression
|
||||
a = if 2 > 1
|
||||
3
|
||||
else
|
||||
4
|
||||
end
|
||||
|
||||
a #=> 3
|
||||
|
||||
# Ternary if
|
||||
a = 1 > 2 ? 3 : 4 #=> 4
|
||||
|
||||
# Case statement
|
||||
cmd = "move"
|
||||
|
||||
action = case cmd
|
||||
when "create"
|
||||
"Creating..."
|
||||
when "copy"
|
||||
"Copying..."
|
||||
when "move"
|
||||
"Moving..."
|
||||
when "delete"
|
||||
"Deleting..."
|
||||
end
|
||||
|
||||
action #=> "Moving..."
|
||||
|
||||
# Loops
|
||||
index = 0
|
||||
while index <= 3
|
||||
puts "Index: #{index}"
|
||||
index += 1
|
||||
end
|
||||
# Index: 0
|
||||
# Index: 1
|
||||
# Index: 2
|
||||
# Index: 3
|
||||
|
||||
index = 0
|
||||
until index > 3
|
||||
puts "Index: #{index}"
|
||||
index += 1
|
||||
end
|
||||
# Index: 0
|
||||
# Index: 1
|
||||
# Index: 2
|
||||
# Index: 3
|
||||
|
||||
# But the preferable way is to use each
|
||||
(1..3).each do |index|
|
||||
puts "Index: #{index}"
|
||||
end
|
||||
# Index: 0
|
||||
# Index: 1
|
||||
# Index: 2
|
||||
# Index: 3
|
||||
|
||||
# Variable's type depends on the type of the expression
|
||||
# in control statements
|
||||
if a < 3
|
||||
a = "hello"
|
||||
else
|
||||
a = true
|
||||
end
|
||||
typeof a #=> (Bool | String)
|
||||
|
||||
if a && b
|
||||
# here both a and b are guaranteed not to be Nil
|
||||
end
|
||||
|
||||
if a.is_a? String
|
||||
a.class #=> String
|
||||
end
|
||||
|
||||
# Functions
|
||||
|
||||
def double(x)
|
||||
x * 2
|
||||
end
|
||||
|
||||
# Functions (and all blocks) implicitly return the value of the last statement
|
||||
double(2) #=> 4
|
||||
|
||||
# Parentheses are optional where the call is unambiguous
|
||||
double 3 #=> 6
|
||||
|
||||
double double 3 #=> 12
|
||||
|
||||
def sum(x, y)
|
||||
x + y
|
||||
end
|
||||
|
||||
# Method arguments are separated by a comma
|
||||
sum 3, 4 #=> 7
|
||||
|
||||
sum sum(3, 4), 5 #=> 12
|
||||
|
||||
# yield
|
||||
# All methods have an implicit, optional block parameter
|
||||
# it can be called with the 'yield' keyword
|
||||
|
||||
def surround
|
||||
puts '{'
|
||||
yield
|
||||
puts '}'
|
||||
end
|
||||
|
||||
surround { puts "hello world" }
|
||||
|
||||
# {
|
||||
# hello world
|
||||
# }
|
||||
|
||||
|
||||
# You can pass a block to a function
|
||||
# "&" marks a reference to a passed block
|
||||
def guests(&block)
|
||||
block.call "some_argument"
|
||||
end
|
||||
|
||||
# You can pass a list of arguments, which will be converted into an array
|
||||
# That's what splat operator ("*") is for
|
||||
def guests(*array)
|
||||
array.each { |guest| puts guest }
|
||||
end
|
||||
|
||||
# If a method returns an array, you can use destructuring assignment
|
||||
def foods
|
||||
["pancake", "sandwich", "quesadilla"]
|
||||
end
|
||||
breakfast, lunch, dinner = foods
|
||||
breakfast #=> "pancake"
|
||||
dinner #=> "quesadilla"
|
||||
|
||||
# By convention, all methods that return booleans end with a question mark
|
||||
5.even? # false
|
||||
5.odd? # true
|
||||
|
||||
# And if a method ends with an exclamation mark, it does something destructive
|
||||
# like mutate the receiver. Some methods have a ! version to make a change, and
|
||||
# a non-! version to just return a new changed version
|
||||
company_name = "Dunder Mifflin"
|
||||
company_name.gsub "Dunder", "Donald" #=> "Donald Mifflin"
|
||||
company_name #=> "Dunder Mifflin"
|
||||
company_name.gsub! "Dunder", "Donald"
|
||||
company_name #=> "Donald Mifflin"
|
||||
|
||||
|
||||
# Define a class with the class keyword
|
||||
class Human
|
||||
|
||||
# A class variable. It is shared by all instances of this class.
|
||||
@@species = "H. sapiens"
|
||||
|
||||
# type of name is String
|
||||
@name : String
|
||||
|
||||
# Basic initializer
|
||||
# Assign the argument to the "name" instance variable for the instance
|
||||
# If no age given, we will fall back to the default in the arguments list.
|
||||
def initialize(@name, @age = 0)
|
||||
end
|
||||
|
||||
# Basic setter method
|
||||
def name=(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
# Basic getter method
|
||||
def name
|
||||
@name
|
||||
end
|
||||
|
||||
# The above functionality can be encapsulated using the attr_accessor method as follows
|
||||
property :name
|
||||
|
||||
# Getter/setter methods can also be created individually like this
|
||||
getter :name
|
||||
setter :name
|
||||
|
||||
# A class method uses self to distinguish from instance methods.
|
||||
# It can only be called on the class, not an instance.
|
||||
def self.say(msg)
|
||||
puts msg
|
||||
end
|
||||
|
||||
def species
|
||||
@@species
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Instantiate a class
|
||||
jim = Human.new("Jim Halpert")
|
||||
|
||||
dwight = Human.new("Dwight K. Schrute")
|
||||
|
||||
# Let's call a couple of methods
|
||||
jim.species #=> "H. sapiens"
|
||||
jim.name #=> "Jim Halpert"
|
||||
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
|
||||
jim.name #=> "Jim Halpert II"
|
||||
dwight.species #=> "H. sapiens"
|
||||
dwight.name #=> "Dwight K. Schrute"
|
||||
|
||||
# Call the class method
|
||||
Human.say("Hi") #=> print Hi and returns nil
|
||||
|
||||
# Variables that start with @ have instance scope
|
||||
class TestClass
|
||||
@var = "I'm an instance var"
|
||||
end
|
||||
|
||||
# Variables that start with @@ have class scope
|
||||
class TestClass
|
||||
@@var = "I'm a class var"
|
||||
end
|
||||
# Variables that start with a capital letter are constants
|
||||
Var = "I'm a constant"
|
||||
Var = "can't be updated" # Already initialized constant Var
|
||||
|
||||
# Class is also an object in crystal. So class can have instance variables.
|
||||
# Class variable is shared among the class and all of its descendants.
|
||||
|
||||
# base class
|
||||
class Human
|
||||
@@foo = 0
|
||||
|
||||
def self.foo
|
||||
@@foo
|
||||
end
|
||||
|
||||
def self.foo=(value)
|
||||
@@foo = value
|
||||
end
|
||||
end
|
||||
|
||||
# derived class
|
||||
class Worker < Human
|
||||
end
|
||||
|
||||
Human.foo #=> 0
|
||||
Worker.foo #=> 0
|
||||
|
||||
Human.foo = 2 #=> 2
|
||||
Worker.foo #=> 0
|
||||
|
||||
Worker.foo = 3 #=> 3
|
||||
Human.foo #=> 2
|
||||
Worker.foo #=> 3
|
||||
|
||||
module ModuleExample
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
end
|
||||
|
||||
# Including modules binds their methods to the class instances
|
||||
# Extending modules binds their methods to the class itself
|
||||
|
||||
class Person
|
||||
include ModuleExample
|
||||
end
|
||||
|
||||
class Book
|
||||
extend ModuleExample
|
||||
end
|
||||
|
||||
Person.foo # => undefined method 'foo' for Person:Class
|
||||
Person.new.foo # => 'foo'
|
||||
Book.foo # => 'foo'
|
||||
Book.new.foo # => undefined method 'foo' for Book
|
||||
|
||||
|
||||
# Exception handling
|
||||
|
||||
# Define new exception
|
||||
class MyException < Exception
|
||||
end
|
||||
|
||||
# Define another exception
|
||||
class MyAnotherException < Exception; end
|
||||
|
||||
ex = begin
|
||||
raise MyException.new
|
||||
rescue ex1 : IndexError
|
||||
"ex1"
|
||||
rescue ex2 : MyException | MyAnotherException
|
||||
"ex2"
|
||||
rescue ex3 : Exception
|
||||
"ex3"
|
||||
rescue ex4 # catch any kind of exception
|
||||
"ex4"
|
||||
end
|
||||
|
||||
ex #=> "ex2"
|
||||
|
||||
```
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Official Documentation](http://crystal-lang.org/)
|
Loading…
Reference in New Issue
Block a user