Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Data structures 如何在OCaml中轻松更改嵌套选项的值?_Data Structures_Ocaml_Optional_Matching - Fatal编程技术网

Data structures 如何在OCaml中轻松更改嵌套选项的值?

Data structures 如何在OCaml中轻松更改嵌套选项的值?,data-structures,ocaml,optional,matching,Data Structures,Ocaml,Optional,Matching,我有以下block的数据结构,它可以有一个库存,可以包含一堆物品: type item = Stone | Sand type stack = { item : item; size : int } type inventory = { inA : stack option; inB : stack option; out : stack option; prc : item option } type blockType = Container | Solid typ

我有以下block的数据结构,它可以有一个库存,可以包含一堆物品:

type item = Stone | Sand
type stack = {
  item : item;
  size : int
}
type inventory = {
  inA : stack option;
  inB : stack option;
  out : stack option;
  prc : item option
}
type blockType = Container | Solid 
type block = {
  blockType : blockType;
  pos : vec;
  oriented : vec option;
  netID : int option;
  sysID : int option;
  inv : inventory option;
  isMachine : bool
}
理论上,我想写一个非常简单的函数,它只需将一个项目添加到blocks库存的out槽中

代码

我甚至使用这些heler函数来避免多个嵌套匹配块

let is_some v = 
  match v with
  | None -> false
  | Some _ -> true

let get_some v = 
  match v with
  | None   -> raise (Er "no some")
  | Some s -> s

我如何才能以更优雅的方式完成这项工作?

以下是我可能会写的内容:

let addItem block =
    let newstack () = Some { item = Stone; size = 1 } in
    let newinv () =
        Some {
            inA = None; inB = None; out = newstack (); prc = None
        }
    in
    match block.inv with
    | None ->
        { block with inv = newinv () }
    | Some ({ out = None; _ } as inv) ->
        { block with inv = Some { inv with out = newstack () } }
    | Some ({ out = Some stack; _ } as inv) ->
        { block with inv =
            Some { inv with
                out = Some { stack with size = stack.size + 1 }
            }
        }
优雅是主观的。但在我看来,关键是使用模式匹配来实现它的好处。您的代码似乎避免了不必要的模式匹配


FWIW,如果block.inv为None,代码将失败。还有b.inv,您可能需要block.inv.

以下是我可能会写的内容:

let addItem block =
    let newstack () = Some { item = Stone; size = 1 } in
    let newinv () =
        Some {
            inA = None; inB = None; out = newstack (); prc = None
        }
    in
    match block.inv with
    | None ->
        { block with inv = newinv () }
    | Some ({ out = None; _ } as inv) ->
        { block with inv = Some { inv with out = newstack () } }
    | Some ({ out = Some stack; _ } as inv) ->
        { block with inv =
            Some { inv with
                out = Some { stack with size = stack.size + 1 }
            }
        }
优雅是主观的。但在我看来,关键是使用模式匹配来实现它的好处。您的代码似乎避免了不必要的模式匹配


FWIW,如果block.inv为None,代码将失败。另外,您可能需要block.inv.的b.inv.

您能解释一下库存类型字段的含义吗?所有的选择都很糟糕。。。理想的数据结构仅限制有效状态的表示。如果我能更好地理解inB…,我可以尝试想象另一种只支持有效数据状态的数据结构方案。这么多的可选字段,我感觉不到

请您解释一下库存类型字段的含义好吗?所有的选择都很糟糕。。。理想的数据结构仅限制有效状态的表示。如果我能更好地理解inB…,我可以尝试想象另一种只支持有效数据状态的数据结构方案。有这么多的可选字段,我不觉得简化代码有什么好处,您需要的是处理选项的助手函数。考虑这个辅助函数:

let (>>=) o f =
  match o with
  | None -> None
  | Some x -> f x
这定义了一个运算符,该运算符在其左侧接受一个选项。如果该选项为“无”,则该运算符的计算结果为“无”,输入无变化。如果选项是某个x,则操作员将其右侧的功能应用于x。由于操作符已经可能计算为选项None,显然函数也必须这样做。此运算符使您可以轻松地使用和链接函数,这些函数不允许任何函数通过它们或对选项的内容进行操作

这导致:

let alter_inv block f =
  { block with inv = block.inv >>= f }

let alter_out block f =
  alter_inv block (fun inv ->
      Some { inv with out = inv.out >>= f })

let add_item block =
  alter_out block (fun out -> Some { out with size = out.size + 1 })

let set_item block item =
  alter_inv block (fun inv ->
      match inv.out with
      | None -> Some { inv with out = Some { item; size = 1 } }
      | Some _ -> Some inv)
给定一个没有资源清册或out槽的块,add_item块将返回未更改的块。给定一个有库存的块和在out槽中的东西,add_item block会增加计数

然而,你在评论中说

实际上,[无错误时引发]不应该是一个问题,因为函数只会在不是这种情况下使用块调用


也就是说,代码的这一部分并没有真正处理选项。您的类型不再符合实际情况,并且您放弃了类型检查器的安全性:您说,您的代码的这一部分在运行时不会出现任何错误,不是因为编译器证明它不会通过类型系统,而是因为您已经彻底检查了逻辑。如果您稍后在该逻辑中引入错误,编译器将无法警告您;您唯一需要注意的是一个意外的运行时错误。

为了简化代码,您找到了可以处理选项的帮助函数。考虑这个辅助函数:

let (>>=) o f =
  match o with
  | None -> None
  | Some x -> f x
这定义了一个运算符,该运算符在其左侧接受一个选项。如果该选项为“无”,则该运算符的计算结果为“无”,输入无变化。如果选项是某个x,则操作员将其右侧的功能应用于x。由于操作符已经可能计算为选项None,显然函数也必须这样做。此运算符使您可以轻松地使用和链接函数,这些函数不允许任何函数通过它们或对选项的内容进行操作

这导致:

let alter_inv block f =
  { block with inv = block.inv >>= f }

let alter_out block f =
  alter_inv block (fun inv ->
      Some { inv with out = inv.out >>= f })

let add_item block =
  alter_out block (fun out -> Some { out with size = out.size + 1 })

let set_item block item =
  alter_inv block (fun inv ->
      match inv.out with
      | None -> Some { inv with out = Some { item; size = 1 } }
      | Some _ -> Some inv)
给定一个没有资源清册或out槽的块,add_item块将返回未更改的块。给定一个有库存的块和在out槽中的东西,add_item block会增加计数

然而,你在评论中说

实际上,[无错误时引发]不应该是一个问题,因为函数只会在不是这种情况下使用块调用


也就是说,代码的这一部分并没有真正处理选项。您的类型不再符合实际情况,并且您放弃了类型检查器的安全性:您说,您的代码的这一部分在运行时不会出现任何错误,不是因为编译器证明它不会通过类型系统,而是因为您已经彻底检查了逻辑。如果您稍后在该逻辑中引入错误,编译器将无法警告您;您唯一需要注意的是意外的运行时错误。

如果您的问题是如何以更优雅的方式执行此操作,那么这可能是一个很好的问题。如果您的问题是如何执行此操作
你可以用一种更优雅的方式来回答这个问题,那么这可能是一个很好的问题。我知道如果block.inv为None,我的代码将失败。在实践中,这不应该是一个问题,因为函数只会在不是这种情况下用块调用。但这不是好的事业实践。是的,我把b搞砸了,把它复制到了THX上,这好多了。我知道如果block.inv为None,我的代码将失败。在实践中,这不应该是一个问题,因为函数只会在不是这种情况下用块调用。但这不是好的事业实践。是的,我把b和块弄乱了,我把它复制到了thxsimple上,表示可选的是代表一个空槽,我明白了,这个空槽后面有什么意思吗?就像从未使用过的块一样,在活动块中,…它只是意味着这个插槽中当前没有堆栈,但可能有一个。但我已经根据我得到的所有小费计算出来了。总之,简单地说,可选的只是代表一个空插槽。我明白,这个空插槽背后有什么意义吗?就像从未使用过的块一样,在活动块中,…它只是意味着这个插槽中当前没有堆栈,但可能有一个。但我已经根据我得到的所有小费计算出来了。无论如何,我认为用这种方式定义>>=会更实际一些,让>>=OF=将o与| None->None | Some x->Some fx匹配。它将some添加到操作符的定义中,并允许您从其他操作符中删除它functions@ghilesZ这个定义通常被称为map,而且灵活性稍差,因为它不允许您将选项从“一些”更改为“无”。我认为用这种方式定义>>=会更实用一些,让>>=OF=将o与|无->无|某些x->某些FX匹配。它将some添加到操作符的定义中,并允许您从其他操作符中删除它functions@ghilesZ该定义通常称为map,灵活性稍差,因为它不允许您将选项从一些更改为无。