Dobrym sposobem myślenia o to „mieć zmieniłem coś, co każdy później kod (łącznie z ponownym uruchomieniem tę samą funkcję później) może kiedykolwiek ewentualnie Zobacz inne niż wartość I wracam? Jeśli tak, to efekt uboczny. Jeśli nie, to możesz wiedzieć, że go nie ma.
Tak, coś takiego:
let inc_nosf v = v+1
nie ma skutków ubocznych, ponieważ tylko zwraca nową wartość, która jest o jeden więcej niż w całkowitej v Więc jeśli uruchomić następujący kod w SML Toplevel, masz. odpowiednie wyniki:
# let x = 5;;
val x : int = 5
# inc_nosf x;;
- : int = 6
# x;;
- : int = 5
Jak widać, wartość x nie uległa zmianie. Ponieważ nie zapisaliśmy wartości zwracanej, nic tak naprawdę nie zostało zwiększone. Nasza funkcja jedynie modyfikuje wartość zwracaną, a nie samą x. Tak, aby zapisać go w X, to mamy do czynienia:
# let x = inc_nosf x;;
val x : int = 6
# x;;
- : int = 6
Ponieważ funkcja inc_nosf nie ma skutków ubocznych (to znaczy, że tylko komunikuje się ze światem zewnętrznym za pomocą jego wartości zwracanej, a nie jakichkolwiek innych zmiany).
Ale coś takiego:
let inc_sf r = r := !r+1
ma skutki uboczne, ponieważ zmienia wartość przechowywaną w odniesieniu reprezentowana przez r. Więc jeśli uruchomić podobny kod na najwyższym poziomie, to masz, zamiast:
# let y = ref 5;;
val y : int ref = {contents = 5}
# inc_sf y;;
- : unit =()
# y;;
- : int ref = {contents = 6}
więc, w tym przypadku, chociaż wciąż nie zapisać zwracanej wartości, to i tak dostał zwiększany. Oznacza to, że musiały nastąpić zmiany w czymś innym niż wartość zwracana. W tym przypadku zmiana polegała na tym, że zmieniono zapisaną wartość ref.
Zgodnie z dobrą zasadą, w Ocaml, jeśli unikasz używania refingów, rekordów, klas, łańcuchów, tablic i tabel hash, unikniesz ryzyka wystąpienia efektów ubocznych. Chociaż można bezpiecznie używać literałów łańcuchowych, o ile unika się modyfikowania ciągu znaków za pomocą funkcji takich jak String.set lub String.fill. Zasadniczo każda funkcja, która może modyfikować typ danych w miejscu, wywoła efekt uboczny.
Jedna wskazówka dla stylu: Wzorzec "if expression then true false" lub podobne wzory są bardzo popularne wśród początkujących. Jeśli się nad tym zastanowisz, dla części, która ma być wybrana, wyrażenie musi być prawdziwe, a dla części else musi być fałszywe. Tak więc ten wzór można upuścić i zredukować do "wyrażenia". – LiKao
Czy możesz pokazać kod dla tego? Zdecydowanie jestem początkującym i mogę użyć wskazówek. –
Oczywiście, jest to dość proste: zamiast "if List.istnieje ((=) 7) myList wtedy true else false ;;" możesz po prostu napisać "List.exists ((=) 7) myList ;;". Jeśli przestaniesz myśleć o powodach, dlaczego te dwa stwierdzenia mają tę samą semantykę, dowiesz się dużo o programowaniu funkcjonalnym (a także ogólnym). – LiKao