;;
;
average
function on C and add local variables to it (compare it with an example in the last chapter).double average (double a, double b) { double sum = a + b; return sum / 2; }
let average ab = let sum = a +. b in sum /. 2.0 ;;
let name = expression in
used to define a local named expression and name
can be used in the future instead of expression up to ;;
, which means the end of the local code block. Notice we didn’t even use the indent after the in declaration. Just take the let ... in
as if it were a single statement.sum
in C code uses memory on the stack. You can later assign the sum any value you want, or even get a memory address where the value is stored. For the OCaml version this is not the case. In it, sum
is just the short name of the expression a +. b
a +. b
. There is no way to assign anything to a sum
. (A little later we will show how to create variables for which you can change the value).let fab = (a +. b) +. (a +. b) ** 2. ;;
let fab = let x = a +. b in x +. x ** 2. ;;
x
in the second example is just a short name for a +. b
a +. b
.let html = let content = read_whole_file file in GHtml.html_from_string content ;; let menu_bold () = match bold_button # active with true -> html # set_font_style ~ enable: [`BOLD] () | false -> html # set_font_style ~ disable: [`BOLD] () ;; let main () = (* code omitted *) factory # add_item "Cut" ~ key: _X ~ callback: html # cut ;;
html
- a widget for editing HTML (a library object lablgtk), created at the beginning of the program with the expression let html =
. Then, further, it is used in several functions.html
in the code snippet above should not be taken as a real global variable, as in C or other imperative languages. There is no memory allocation for "storing" a " html
pointer." It is impossible to assign html
something, for example, to redefine it as a pointer to another widget. In the next section, we will discuss links that are real variables.let ...
, no matter where, at the top level (globally) or inside functions (locally), is often called let-binding (binding using let).int
link in OCaml:ref 0 ;;
let my_ref = ref 0 ;;
my_ref: = 100 ;;
#! my_ref ;; -: int = 100
:=
used to assign links, and the operator !
dereference links returning content. Here is a rough but effective comparison with C / C ++:Ocaml | C / C ++ |
---|---|
let my_ref = ref 0 ;; my_ref: = 100 ;; ! my_ref | int a = 0; int * my_ptr = & a; * my_ptr = 100; * my_ptr |
let name=expression in
to name local expressions in your functions will be much more frequent.A nested function is a function defined inside another function (Nested functions are not supported for GNU C ++). The names of nested functions are local to the block in which they were defined. For example, here is the definition of the nested function 'square', which is called twice:foo (double a, double b) { double square (double z) {return z * z; } return square (a) + square (b); }
The nested function has access to all the functions of the external function that are visible at the time of the function definition. This is the so-called "area of ​​lexical visibility." Here is an example of a nested function that inherits a variable called offset:bar (int * array, int offset, int size) { int access (int * array, int index) {return array [index + offset]; } int i; / * ... * / for (i = 0; i <size; i ++) / * ... * / access (array, i) / * ... * / }
let read_whole_channel chan = let buf = Buffer.create 4096 in let rec loop () = let newline = input_line chan in Buffer.add_string buf newline; Buffer.add_char buf '\ n'; loop () in try loop () with End_of_file -> Buffer.contents buf ;;
loop
function, which takes an argument of type unit
. You can call loop ()
from the read_whole_channel function, and it is not defined outside the function. The nested function has access to the variables defined in the main function (so the loop
gets access to the local variables buf
and chan
).let name arguments = function-defenition in.
let rec
instead of let
if the function is recursive (as in the example above).open
commandGraphics
.Graphics
module consists of 5 files (on my system):/usr/lib/ocaml/3.08/graphics.a /usr/lib/ocaml/3.08/graphics.cma /usr/lib/ocaml/3.08/graphics.cmi /usr/lib/ocaml/3.08/graphics.cmxa /usr/lib/ocaml/3.08/graphics.mli
graphics.mli
. This is a text file, so you can easily see its contents. First of all, note that the name is graphics.mli
, not Graphics.mli
. OCaml always makes the first letter of the file name capitalized when it comes to modules. This can be quite confusing, unless you know about it beforehand.Graphics
, then there are two ways. Or at the beginning of our program we write the open Graphics;;
declaration open Graphics;;
or we supplement all calls to the corresponding functions with the prefix: Graphics.open_graph.open
. open
slightly resembles the import
function in Java, and slightly more (recalls) the use
expression in Perl.open_graph
, the second one calls Graphics.open_graph
. [approx. Trans .: At the beginning of the article screenshots of what programs do].(* To compile this example: ocamlc graphics.cma grtest1.ml -o grtest1 *) open Graphics ;; open_graph "640x480" ;; for i = 12 downto 1 do let radius = i * 20 in set_color (if (i mod 2) = 0 then red else yellow); fill_circle 320 240 radius done ;; read_line () ;;
(* To compile this example: ocamlc graphics.cma grtest2.ml -o grtest2 *) Random.self_init () ;; Graphics.open_graph "640x480" ;; let rec iterate r x_init i = if i = 1 then x_init else let x = iterate r x_init (i-1) in r *. x *. (1.0 -. X) ;; for x = 0 to 639 do let r = 4.0 *. (float_of_int x) /. 640.0 in for i = 0 to 39 do let x_init = Random.float 1.0 in let x_final = iterate r x_init 500 in let y = int_of_float (x_final *. 480.) in Graphics.plot xy done done ;; read_line () ;;
for
loops, if-then-else
blocks, recursion. We will discuss this later. Regardless, you can still: (1) try to understand how they work (2) how type inference allows you to catch errors.Pervasives
modulePervasives
module (located in /usr/lib/ocaml/3.08/pervasives.mli [note: I have /usr/lib/ocaml/pervasives.mli]). All characters from the Pervasives
module are automatically imported into all OCaml programs.Graphics
, but don't want to import them all, and you are lazy to type Graphics every time? Just rename the module using this trick:module Gr = Graphics ;; Gr.open_graph "640x480" ;; Gr.fill_circle 320 240 240 ;; read_line () ;;
;;
and ;
;;
when to use ;
and when should you skip both options? This tricky question remains as long as you understand it well. He was worried about the author for a long time while he was studying OCaml.;;
to separate statements in the top level of your code (top-level) and never inside the definition of functions or any other kind of statements.Random.self_init () ;; Graphics.open_graph "640x480" ;; let rec iterate r x_init i = if i = 1 then x_init else let x = iterate r x_init (i-1) in r *. x *. (1.0 -. X) ;;
iterate
function. Each of them ends ;;
.;;
. As a neophyte, you should not worry too much about this rule and always write ;;
, as prescribed by rule number 1. But if you read someone else's code, you will occasionally encounter the absence ;;
. Places to drop ;;
:let
keyword;open
keyword;type
;;;
omitted where possible:open Random (* ;; *) open Graphics ;; self_init () ;; open_graph "640x480" (* ;; *) let rec iterate r x_init i = if i = 1 then x_init else let x = iterate r x_init (i-1) in r *. x *. (1.0 -. X) ;; for x = 0 to 639 do let r = 4.0 *. (float_of_int x) /. 640.0 in for i = 0 to 39 do let x_init = Random.float 1.0 in let x_final = iterate r x_init 500 in let y = int_of_float (x_final *. 480.) in Graphics.plot xy done done ;; read_line () (* ;; *)
;
. They are completely different from the rules for ;;
. A single semicolon (;) is called a sequence point . Trans .: I can be wrong with the translation of the sequence point], which plays exactly the same role as a single semicolon in C, C ++, Java or Pearl. It means "do everything after this place when you do everything before it." I bet you didn't know that.let ... in
statement and never put ;
after him.;
except the last one.;
in the code.for i = 0 to 39 do let x_init = Random.float 1.0 in let x_final = iterate r x_init 500 in let y = int_of_float (x_final *. 480.) in Graphics.plot xy done
;
- this is a line of Graphics.plot xy
, but since this is the last line of the block, according to rule No. 4, it is not necessary to install it.;
- the same operator as, for example, the addition operator (+
). Well, not quite as +, but in fact - exactly. + has the typeint -> int -> int
- it takes two integers and returns an integer (their sum).;
has typeunit -> 'b -> 'b
- it takes two values ​​and simply returns the second. Unlike the comma in C. You can writea;b;c;d
as easy as you can writea + b + c + d
.
This is one of those fundamental concepts, an understanding of which gives an understanding of the language, but which never really speaks out loud — literally everything in OCaml is an expression.if/then/else
is an expression.a;b
is an expression.match foo with ...
is an expression. The code below is absolutely correct (and they all do the same thing):let fxby = if b then x + y else x + 0 let fxby = x + (if b then y else 0) let fxby = x + (match b with true -> y | false -> 0) let fxby = x + (let gz = function true -> z | false -> 0 in gyb) let fxby = x + (let _ = y + 3 in (); if b then y else 0)
Especially look at the last one - I use;
as an operator for “combining two statements. All functions in OCaml can be expressed as:let name [parameters] = expression
The definition of "expression" in OCaml is somewhat broader than in C. In fact, C has the concept of "statements", but all the statements in C are just expressions in OCaml (combined;
).
The only difference between;
and+
is the ability to reference+
as a function. For example, I can define the functionsum_list
to sum up lists of integers as:let sum_list = List.fold_left (+) 0
;;
where they used ;
where they used open
, how they beat off text, how they used local and global expressions.?foo
and ~foo
are ways to specify optional and named arguments to functions in OCaml. It will not be possible to draw a direct parallel with C-like languages, but Perl, Python and Smalltalk have this concept. You can call the arguments in the function call, omit some of them and pass the remaining ones in the order that suits you.foo#bar
- a method call (a method call called bar
with respect to an object called foo
). This is similar to foo->bar
, foo.bar
, $foo->bar
(C ++, Java, Perl).;;
because the following keywords are open or let). It also creates a function called file_dialog. Inside this function, it defines a named expression called sel
using the two-line statement let sel = .. in
. It then calls several methods for sel.(* First snippet *) open StdLabels open GMain let file_dialog ~ title ~ callback? filename () = let sel = GWindow.file_selection ~ title ~ modal: true? Filename () in sel # cancel_button # connect # clicked ~ callback: sel # destroy; sel # ok_button # connect # clicked ~ callback: do_ok; sel # show ()
;;
according to rule number 2.(* Second snippet *) let window = GWindow.window ~ width: 500 ~ height: 300 ~ title: "editor" () let vbox = GPack.vbox ~ packing: window # add () let menubar = GMenu.menu_bar ~ packing: vbox # pack () let factory = new GMenu.factory menubar let accel_group = factory # accel_group let file_menu = factory # add_submenu "File" let edit_menu = factory # add_submenu "Edit" let hbox = GPack.hbox ~ packing: vbox # add () let editor = new editor ~ packing: hbox # add () let scrollbar = GRange.scrollbar `VERTICAL ~ packing: hbox # pack ()
GdkKesyms
module. Then comes the unusual let-binding
. let _ = expression
means “calculate the value of the expression (including the execution of all side effects), but throw out the result”. In this case, “calculate the value of an expression” means executing Main.main ()
, which is the main GTK loop, a side effect of which is the appearance of a window on the screen and the execution of the entire application. The “result” of the call to Main.main ()
significant. Most likely this unit
, but I did not check - and it just does not return until the application is completed.(* Third snippet *) open GdkKeysyms let _ = window # connect # destroy ~ callback: Main.quit; let factory = new GMenu.factory file_menu ~ accel_group in factory # add_item "Open ..." ~ key: _O ~ callback: editor # open_file; factory # add_item "Save" ~ key: _S ~ callback: editor # save_file; factory # add_item "Save as ..." ~ callback: editor # save_dialog; factory # add_separator (); factory # add_item "Quit" ~ key: _Q ~ callback: window # destroy; let factory = new GMenu.factory edit_menu ~ accel_group in factory # add_item "Copy" ~ key: _C ~ callback: editor # text # copy_clipboard; factory # add_item "Cut" ~ key: _X ~ callback: editor # text # cut_clipboard; factory # add_item "Paste" ~ key: _V ~ callback: editor # text # paste_clipboard; factory # add_separator (); factory # add_check_item "Word wrap" ~ active: false ~ callback: editor # text # set_word_wrap; factory # add_check_item "Read only" ~ active: false ~ callback: (fun b -> editor # text # set_editable (not b)); window # add_accel_group accel_group; editor # text # event # connect # button_press ~ callback: (fun ev -> let button = GdkEvent.Button.button ev in if button = 3 then begin file_menu # popup ~ button ~ time: (GdkEvent.Button.time ev); true end else false); editor # text # set_vadjustment scrollbar # adjustment; window # show (); Main.main ()
Source: https://habr.com/ru/post/108532/
All Articles