![](/img/trans.png)
[英]In Scala Functional Programming, is there an idiomatic way to map with a state?
[英]Is there an idiomatic way to do implicit local state in OCaml?
我想編寫一些使用某種本地狀態構建東西的代碼。 例如,請考慮以下使用本地狀態生成順序整數的代碼:
type state = int ref
let uniqueId : (state -> int) =
fun s -> incr s; !s
let makeThings : ((state -> 'a) -> 'a) =
fun body -> body (ref 0)
let () =
let x1 = makeThings(fun s ->
let i = uniqueId s in (* 1 *)
i
) in
print_int x1; print_newline (); (* Prints 1 *)
(* Each makeThings callback gets its own local state.
The ids start being generated from 1 again *)
let x2 = makeThings(fun s ->
let i = uniqueId s in (* 1 *)
let j = uniqueId s in (* 2 *)
i + j
) in
print_int x2; print_newline (); (* Prints 3 *)
()
我很好奇,如果有一種方法,使s
狀態參數的內部,makeThings回調隱含的,所以我並不需要一遍又一遍,因此鍵入它保證所有的uniqueId
電話獲得通過相同的狀態prameter。 例如,在Haskell中,你可以使用monad和do-notation來結束代碼
makeThings $ do
i <- uniqueId
j <- uniqueId
return (i + j)
在Ocaml中,我想到的唯一的事情就是使s
成為一個全局變量(非常不受歡迎)或試圖模擬Haskell的monadic接口,我擔心這將是很多工作並最終導致緩慢的代碼也很丑陋到期缺乏記號。 有沒有我沒想過的替代方案?
Monads也在OCaml工作。 由於pa_monad_custom語法擴展,您甚至可以使用do-notation。 雖然,在大多數情況下只有一個中綴綁定運算符,即>>=
就足以編寫一個花哨的代碼。
您的代碼看起來像monadic樣式+引用的奇怪混合...如果您想限制僅通過特定方式更改本地狀態,您應該在本地上下文中隱藏它們:
let make_unique_id init =
let s = ref (init - 1) (* :-) *) in
fun () ->
incr s;
!s
s
現在隱藏在閉包中。 因此,您可以創建彼此獨立的計數器:
let () =
let x1 =
let unique_id = make_unique_id 1 in
let i = unique_id () in
i
in
print_int x1; print_newline (); (* Prints 1 *)
let x2 =
let unique_id = make_unique_id 1 in
let i = unique_id () in (* 1 *)
let j = unique_id () in (* 2 *)
i + j
in
print_int x2; print_newline () (* Prints 3 *)
你已經擁有的東西略有不同。 而不是使用延續,只需提供一個函數來生成一個新的狀態:
module State : sig
type t
val fresh : unit -> t
val uniqueId : t -> int
end = struct
type t = int ref
let fresh () = ref 0
let uniqueId s = incr s; !s
end
let () =
let x1 =
let s = State.fresh () in
let i = State.uniqueId s in
i
in
print_int x1;
print_newline () (* Prints 1 *)
let () =
let x2 =
let s = State.fresh () in
let i = State.uniqueId s in (* 1 *)
let j = State.uniqueId s in (* 2 *)
i + j
in
print_int x2;
print_newline () (* Prints 3 *)
這是處理編譯器環境的常用方法,看起來很像你想要做的事情。 它不會隱式地通過狀態,因為OCaml不支持隱式參數。 但是,如果您只需要一個這樣的“環境”參數,那么將它添加到所有適當的函數並不是太繁重。
我想你想要達到的目標是:
state
函數f
(make_things)。 f
,狀態都會重置 f
一次調用中,狀態可以自動改變 如果我是正確的,那么我們不需要Monald,相反,我們可以使用備忘錄 。
let memo_incr_state () =
let s = ref 0 in
fun() -> s := !s + 1; !s
let make_things f =
let ms = memo_incr_state() in
f ms
let f1 ms =
let i = ms() in
let j = ms() in
i+j
let x1 = make_things f1 (* x1 should be 3 *)
基本的想法是我們使用thunk來記住狀態。
有關記憶的更多知識可以從https://realworldocaml.org/v1/en/html/imperative-programming-1.html#memoization-and-dynamic-programming獲得
聽起來你想要一個全局變量
let currentId = ref 0
let uniqueId () =
incr currentId;
!currentId
您建議全局變量是不合需要的,但您指定的行為(“對uniqueId的所有調用都傳遞相同的狀態參數”)正是全局變量的行為。
如果您擔心其他代碼訪問全局變量,那么就不要在模塊的簽名( .mli
文件)中公開currentId
。
如果您擔心訪問currentId
的同一模塊中的其他代碼,那么您可以通過將其放在uniqueId
的定義中來限制其范圍:
let uniqueId =
let currentId = ref 0 in
fun () ->
incr currentId;
!currentId
或創建一個不在簽名中公開currentId
的子模塊:
module M : sig
val uniqueId : unit -> int
end = struct
let currentId = ref 0
let uniqueId () =
incr currentId;
!currentId
end
include M
就個人而言,我會選擇第一個解決方案( .mli
文件隱藏的全局變量)。 確保同一模塊中的其他代碼不會濫用currentId
並且模塊系統保護您免受其他地方的代碼的currentId
並不困難。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.