2014-09-19 03:07:51 +04:00
|
|
|
---
|
2014-09-20 04:55:52 +04:00
|
|
|
language: Nim
|
2014-09-19 03:11:27 +04:00
|
|
|
filename: learnNim.nim
|
2014-09-19 03:07:51 +04:00
|
|
|
contributors:
|
|
|
|
- ["Jason J. Ayala P.", "http://JasonAyala.com"]
|
2023-09-22 12:45:52 +03:00
|
|
|
- ["Dennis Felsing", "https://dennis.felsing.org"]
|
2014-09-19 03:07:51 +04:00
|
|
|
---
|
|
|
|
|
2015-03-01 17:28:37 +03:00
|
|
|
Nim (formerly Nimrod) is a statically typed, imperative programming language
|
2014-09-20 05:26:31 +04:00
|
|
|
that gives the programmer power without compromises on runtime efficiency.
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
Nim is efficient, expressive, and elegant.
|
|
|
|
|
2016-03-23 21:50:24 +03:00
|
|
|
```nim
|
2017-10-20 09:07:29 +03:00
|
|
|
# Single-line comments start with a #
|
|
|
|
|
|
|
|
#[
|
2017-12-27 21:35:16 +03:00
|
|
|
This is a multiline comment.
|
|
|
|
In Nim, multiline comments can be nested, beginning with #[
|
|
|
|
... and ending with ]#
|
2017-10-20 09:07:29 +03:00
|
|
|
]#
|
|
|
|
|
|
|
|
discard """
|
|
|
|
This can also work as a multiline comment.
|
|
|
|
Or for unparsable, broken code
|
|
|
|
"""
|
|
|
|
|
2014-09-20 05:17:59 +04:00
|
|
|
var # Declare (and assign) variables,
|
2014-09-20 04:55:52 +04:00
|
|
|
letter: char = 'n' # with or without type annotations
|
2014-09-20 05:14:51 +04:00
|
|
|
lang = "N" & "im"
|
2020-07-16 19:48:59 +03:00
|
|
|
nLength: int = len(lang)
|
2014-09-19 03:07:51 +04:00
|
|
|
boat: float
|
2014-09-20 04:55:52 +04:00
|
|
|
truth: bool = false
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-20 05:17:59 +04:00
|
|
|
let # Use let to declare and bind variables *once*.
|
2014-09-19 03:07:51 +04:00
|
|
|
legs = 400 # legs is immutable.
|
|
|
|
arms = 2_000 # _ are ignored and are useful for long numbers.
|
2014-09-20 05:14:51 +04:00
|
|
|
aboutPi = 3.15
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
const # Constants are computed at compile time. This provides
|
|
|
|
debug = true # performance and is useful in compile time expressions.
|
|
|
|
compileBadCode = false
|
|
|
|
|
|
|
|
when compileBadCode: # `when` is a compile time `if`
|
|
|
|
legs = legs + 1 # This error will never be compiled.
|
2014-09-20 05:14:51 +04:00
|
|
|
const input = readline(stdin) # Const values must be known at compile time.
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-20 05:14:51 +04:00
|
|
|
discard 1 > 2 # Note: The compiler will complain if the result of an expression
|
2014-09-19 03:07:51 +04:00
|
|
|
# is unused. `discard` bypasses this.
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Data Structures
|
|
|
|
#
|
|
|
|
|
|
|
|
# Tuples
|
|
|
|
|
|
|
|
var
|
|
|
|
child: tuple[name: string, age: int] # Tuples have *both* field names
|
|
|
|
today: tuple[sun: string, temp: float] # *and* order.
|
|
|
|
|
|
|
|
child = (name: "Rudiger", age: 2) # Assign all at once with literal ()
|
|
|
|
today.sun = "Overcast" # or individual fields.
|
2024-05-18 11:26:21 +03:00
|
|
|
today[1] = 70.1 # or by index.
|
|
|
|
|
|
|
|
let impostor = ("Rudiger", 2) # Two tuples are the same as long as they have
|
|
|
|
assert child == impostor # the same type and the same contents
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
# Sequences
|
|
|
|
|
|
|
|
var
|
|
|
|
drinks: seq[string]
|
|
|
|
|
|
|
|
drinks = @["Water", "Juice", "Chocolate"] # @[V1,..,Vn] is the sequence literal
|
|
|
|
|
2015-03-01 17:28:37 +03:00
|
|
|
drinks.add("Milk")
|
|
|
|
|
|
|
|
if "Milk" in drinks:
|
|
|
|
echo "We have Milk and ", drinks.len - 1, " other drinks"
|
|
|
|
|
|
|
|
let myDrink = drinks[2]
|
|
|
|
|
2014-09-19 03:07:51 +04:00
|
|
|
#
|
2014-09-20 05:14:51 +04:00
|
|
|
# Defining Types
|
2014-09-19 03:07:51 +04:00
|
|
|
#
|
|
|
|
|
|
|
|
# Defining your own types puts the compiler to work for you. It's what makes
|
|
|
|
# static typing powerful and useful.
|
|
|
|
|
|
|
|
type
|
2017-08-23 11:14:39 +03:00
|
|
|
Name = string # A type alias gives you a new type that is interchangeable
|
2014-09-20 04:55:52 +04:00
|
|
|
Age = int # with the old type but is more descriptive.
|
2014-09-19 03:07:51 +04:00
|
|
|
Person = tuple[name: Name, age: Age] # Define data structures too.
|
2014-09-20 05:14:51 +04:00
|
|
|
AnotherSyntax = tuple
|
2014-09-19 04:02:33 +04:00
|
|
|
fieldOne: string
|
|
|
|
secondField: int
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
var
|
2014-09-20 05:14:51 +04:00
|
|
|
john: Person = (name: "John B.", age: 17)
|
|
|
|
newage: int = 18 # It would be better to use Age than int
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
john.age = newage # But still works because int and Age are synonyms
|
|
|
|
|
|
|
|
type
|
2014-09-20 04:55:52 +04:00
|
|
|
Cash = distinct int # `distinct` makes a new type incompatible with its
|
2014-09-19 03:07:51 +04:00
|
|
|
Desc = distinct string # base type.
|
|
|
|
|
|
|
|
var
|
2014-09-20 04:55:52 +04:00
|
|
|
money: Cash = 100.Cash # `.Cash` converts the int to our type
|
2014-09-19 04:02:33 +04:00
|
|
|
description: Desc = "Interesting".Desc
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
when compileBadCode:
|
2014-09-19 04:02:33 +04:00
|
|
|
john.age = money # Error! age is of type int and money is Cash
|
|
|
|
john.name = description # Compiler says: "No way!"
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
#
|
|
|
|
# More Types and Data Structures
|
|
|
|
#
|
|
|
|
|
2024-05-18 11:26:21 +03:00
|
|
|
# Objects are similar to tuples, but they *require* names of the fields
|
|
|
|
|
|
|
|
type
|
|
|
|
Room = ref object # reference to an object, useful for big objects or
|
|
|
|
windows: int # objects inside objects
|
|
|
|
doors: int = 1 # Change the default value of a field (since Nim 2.0)
|
|
|
|
House = object
|
|
|
|
address: string
|
|
|
|
rooms: seq[Room]
|
|
|
|
|
|
|
|
var
|
|
|
|
defaultHouse = House() # initialize with default values
|
|
|
|
defaultRoom = new Room() # create new instance of ref object
|
|
|
|
sesameHouse = House(address: "123 Sesame St.", rooms: @[defaultRoom])
|
|
|
|
|
2014-09-20 05:33:21 +04:00
|
|
|
# Enumerations allow a type to have one of a limited number of values
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
type
|
2014-09-20 04:55:52 +04:00
|
|
|
Color = enum cRed, cBlue, cGreen
|
2017-08-23 11:14:39 +03:00
|
|
|
Direction = enum # Alternative formatting
|
2014-09-19 04:02:33 +04:00
|
|
|
dNorth
|
|
|
|
dWest
|
|
|
|
dEast
|
|
|
|
dSouth
|
2014-09-19 03:07:51 +04:00
|
|
|
var
|
2014-09-19 04:02:33 +04:00
|
|
|
orient = dNorth # `orient` is of type Direction, with the value `dNorth`
|
2014-09-20 04:55:52 +04:00
|
|
|
pixel = cGreen # `pixel` is of type Color, with the value `cGreen`
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-19 04:02:33 +04:00
|
|
|
discard dNorth > dEast # Enums are usually an "ordinal" type
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
# Subranges specify a limited valid range
|
|
|
|
|
|
|
|
type
|
|
|
|
DieFaces = range[1..20] # Only an int from 1 to 20 is a valid value
|
|
|
|
var
|
|
|
|
my_roll: DieFaces = 13
|
|
|
|
|
|
|
|
when compileBadCode:
|
|
|
|
my_roll = 23 # Error!
|
|
|
|
|
|
|
|
# Arrays
|
|
|
|
|
|
|
|
type
|
2022-01-16 05:47:26 +03:00
|
|
|
RollCounter = array[DieFaces, int] # Arrays are fixed length and
|
2014-09-19 04:02:33 +04:00
|
|
|
DirNames = array[Direction, string] # indexed by any ordinal type.
|
|
|
|
Truths = array[42..44, bool]
|
2014-09-19 03:07:51 +04:00
|
|
|
var
|
2014-09-19 04:02:33 +04:00
|
|
|
counter: RollCounter
|
2014-09-19 03:07:51 +04:00
|
|
|
directions: DirNames
|
2014-09-19 04:02:33 +04:00
|
|
|
possible: Truths
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-19 04:02:33 +04:00
|
|
|
possible = [false, false, false] # Literal arrays are created with [V1,..,Vn]
|
|
|
|
possible[42] = true
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-19 04:02:33 +04:00
|
|
|
directions[dNorth] = "Ahh. The Great White North!"
|
|
|
|
directions[dWest] = "No, don't go there."
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
my_roll = 13
|
2014-09-19 04:02:33 +04:00
|
|
|
counter[my_roll] += 1
|
|
|
|
counter[my_roll] += 1
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
var anotherArray = ["Default index", "starts at", "0"]
|
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
# More data structures are available, including tables, sets, lists, queues,
|
|
|
|
# and crit bit trees.
|
2015-05-18 12:19:46 +03:00
|
|
|
# http://nim-lang.org/docs/lib.html#collections-and-algorithms
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
#
|
|
|
|
# IO and Control Flow
|
|
|
|
#
|
|
|
|
|
|
|
|
# `case`, `readLine()`
|
|
|
|
|
|
|
|
echo "Read any good books lately?"
|
|
|
|
case readLine(stdin)
|
|
|
|
of "no", "No":
|
|
|
|
echo "Go to your local library."
|
|
|
|
of "yes", "Yes":
|
|
|
|
echo "Carry on, then."
|
|
|
|
else:
|
|
|
|
echo "That's great; I assume."
|
|
|
|
|
2014-09-20 05:14:51 +04:00
|
|
|
# `while`, `if`, `continue`, `break`
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2015-05-18 12:17:45 +03:00
|
|
|
import strutils as str # http://nim-lang.org/docs/strutils.html
|
2014-09-19 03:07:51 +04:00
|
|
|
echo "I'm thinking of a number between 41 and 43. Guess which!"
|
2014-09-20 04:55:52 +04:00
|
|
|
let number: int = 42
|
2014-09-19 03:07:51 +04:00
|
|
|
var
|
|
|
|
raw_guess: string
|
2024-05-13 10:13:58 +03:00
|
|
|
guess: int # Variables in Nim are always initialized with a zero value
|
2014-09-19 04:02:33 +04:00
|
|
|
while guess != number:
|
2014-09-19 03:07:51 +04:00
|
|
|
raw_guess = readLine(stdin)
|
2014-09-20 05:30:11 +04:00
|
|
|
if raw_guess == "": continue # Skip this iteration
|
2014-09-19 03:07:51 +04:00
|
|
|
guess = str.parseInt(raw_guess)
|
|
|
|
if guess == 1001:
|
|
|
|
echo("AAAAAAGGG!")
|
|
|
|
break
|
2014-09-19 04:02:33 +04:00
|
|
|
elif guess > number:
|
2014-09-20 04:55:52 +04:00
|
|
|
echo("Nope. Too high.")
|
2014-09-19 04:02:33 +04:00
|
|
|
elif guess < number:
|
2014-09-20 04:55:52 +04:00
|
|
|
echo(guess, " is too low")
|
2014-09-19 03:07:51 +04:00
|
|
|
else:
|
|
|
|
echo("Yeeeeeehaw!")
|
|
|
|
|
|
|
|
#
|
|
|
|
# Iteration
|
|
|
|
#
|
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
for i, elem in ["Yes", "No", "Maybe so"]: # Or just `for elem in`
|
|
|
|
echo(elem, " is at index: ", i)
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
for k, v in items(@[(person: "You", power: 100), (person: "Me", power: 9000)]):
|
|
|
|
echo v
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
let myString = """
|
2014-09-20 04:55:52 +04:00
|
|
|
an <example>
|
|
|
|
`string` to
|
2014-09-19 03:07:51 +04:00
|
|
|
play with
|
2014-09-20 04:55:52 +04:00
|
|
|
""" # Multiline raw string
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
for line in splitLines(myString):
|
|
|
|
echo(line)
|
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
for i, c in myString: # Index and letter. Or `for j in` for just letter
|
2014-09-20 05:14:51 +04:00
|
|
|
if i mod 2 == 0: continue # Compact `if` form
|
|
|
|
elif c == 'X': break
|
2014-09-20 04:55:52 +04:00
|
|
|
else: echo(c)
|
2014-09-20 05:02:13 +04:00
|
|
|
|
2014-09-19 03:07:51 +04:00
|
|
|
#
|
|
|
|
# Procedures
|
|
|
|
#
|
|
|
|
|
2014-09-19 04:02:33 +04:00
|
|
|
type Answer = enum aYes, aNo
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
proc ask(question: string): Answer =
|
|
|
|
echo(question, " (y/n)")
|
|
|
|
while true:
|
|
|
|
case readLine(stdin)
|
|
|
|
of "y", "Y", "yes", "Yes":
|
2014-09-19 04:02:33 +04:00
|
|
|
return Answer.aYes # Enums can be qualified
|
2014-09-19 03:07:51 +04:00
|
|
|
of "n", "N", "no", "No":
|
2014-09-19 04:02:33 +04:00
|
|
|
return Answer.aNo
|
2014-09-19 03:07:51 +04:00
|
|
|
else: echo("Please be clear: yes or no")
|
|
|
|
|
|
|
|
proc addSugar(amount: int = 2) = # Default amount is 2, returns nothing
|
2015-10-08 04:19:50 +03:00
|
|
|
assert(amount > 0 and amount < 9000, "Crazy Sugar")
|
2014-09-19 03:07:51 +04:00
|
|
|
for a in 1..amount:
|
2014-09-20 04:55:52 +04:00
|
|
|
echo(a, " sugar...")
|
2014-09-19 03:07:51 +04:00
|
|
|
|
|
|
|
case ask("Would you like sugar in your tea?")
|
2014-09-19 04:02:33 +04:00
|
|
|
of aYes:
|
2014-09-19 03:07:51 +04:00
|
|
|
addSugar(3)
|
2014-09-19 04:02:33 +04:00
|
|
|
of aNo:
|
2014-09-19 03:07:51 +04:00
|
|
|
echo "Oh do take a little!"
|
|
|
|
addSugar()
|
2014-09-20 05:02:13 +04:00
|
|
|
# No need for an `else` here. Only `yes` and `no` are possible.
|
2014-09-19 03:07:51 +04:00
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
#
|
|
|
|
# FFI
|
|
|
|
#
|
|
|
|
|
|
|
|
# Because Nim compiles to C, FFI is easy:
|
2014-09-20 05:02:13 +04:00
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
proc strcmp(a, b: cstring): cint {.importc: "strcmp", nodecl.}
|
|
|
|
|
2014-09-20 16:07:06 +04:00
|
|
|
let cmp = strcmp("C?", "Easy!")
|
2014-09-19 03:07:51 +04:00
|
|
|
```
|
|
|
|
|
2014-09-20 04:55:52 +04:00
|
|
|
Additionally, Nim separates itself from its peers with metaprogramming,
|
|
|
|
performance, and compile-time features.
|
|
|
|
|
2014-09-19 03:07:51 +04:00
|
|
|
## Further Reading
|
|
|
|
|
2015-05-18 12:17:45 +03:00
|
|
|
* [Home Page](http://nim-lang.org)
|
|
|
|
* [Download](http://nim-lang.org/download.html)
|
|
|
|
* [Community](http://nim-lang.org/community.html)
|
|
|
|
* [FAQ](http://nim-lang.org/question.html)
|
|
|
|
* [Documentation](http://nim-lang.org/documentation.html)
|
|
|
|
* [Manual](http://nim-lang.org/docs/manual.html)
|
|
|
|
* [Standard Library](http://nim-lang.org/docs/lib.html)
|
|
|
|
* [Rosetta Code](http://rosettacode.org/wiki/Category:Nim)
|