2017-12-29 10 views
1

I 항목의 스택 countain 수있는 재고 할 수 블록의 다음과 같은 데이터 구조를 가지고 :OCaml에서 중첩 된 선택 사항의 값을 쉽게 변경하는 방법은 무엇입니까?

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 
} 

지금 내가 단순히 항목을 추가하는, 이론, 아주 간단한 함수를 작성 할를 " 블록 인벤토리의 "출력"슬롯에 있습니다. 내 매우 자세한 시도는이이었다

let addItem block = 
    let oldInv = get_some b.inv in 
    if is_some oldInv.out 
    then 
     let oldOut = get_some oldInv.out in 
     let newOut = {oldOut with size = oldOut.size+1} in 
     let newInv = {oldInv with out = Some newOut} in 
     {block with inv = Some newInv} 
    else 
     let newInv = {oldInv with out = Some {item=Stone; size=1}} in 
     {block with inv = Some newInv} 

을하고 난 여러 중첩 된 경기 블록

을 방지하기 위해 이러한 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 

어떻게 더 우아한 방법으로이 작업을 수행 할 수 있습니까? 여기

+0

질문이 더 우아한 방법으로 할 수 있다면 [codereview] (https://codereview.stackexchange.com/)에 대한 좋은 질문 일 수 있습니다. – vonaka

답변

1

내가 작성할 수 있습니다 것입니다 :

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.invNone 인 경우 코드가 실패합니다. (또한 을 원하는 b.inv이 있습니다.)

+0

감사합니다. block.inv가 None이면 내 코드가 실패합니다. 실제로는 함수가 블록이 아닌 경우에만 호출되기 때문에 문제가 될 수 없습니다. 하지만 이것은 좋은 원인이 아닙니다. 그리고 내가 엉망으로 엉망진창을 만들었고 –

0

인벤토리 유형 필드의 의미를 설명해 주시겠습니까? 모든 옵션이 악취가납니다 ... 이상적인 데이터 구조는 유효한 상태의 표현만을 제한합니다. InB에서 더 잘 이해할 수 있다면 ... 유효한 데이터 상태 만 가능하게하는 또 다른 데이터 구조 제안을 상상해 볼 수 있습니다. 선택 필드가 너무 많아서 느껴지지 않습니다.

+1

단순히 빈 슬롯을 나타내는 옵션이 있습니다. –

+0

이 빈 슬롯 뒤에는 의미가 있습니까? 결코 블록을 사용하지 않는 것처럼, 활동 블록에서 ... – Aldrik

+0

잘이 슬롯에는 현재 스택이 없지만 하나는있을 수 있음을 의미합니다. 그러나 나는 내가 가지고있는 모든 tipps에 기반하여 이미 그것을 밖으로 내밀었다. 어쨌든 –

2

코드를 단순화하기 위해 옵션에서 작동하는 도우미 함수를 사용했습니다. 이 도우미 함수를 고려해보십시오.

let (>>=) o f = 
    match o with 
    | None -> None 
    | Some x -> f x 

왼쪽에 옵션을 취하는 연산자를 정의합니다. 옵션이 None 인 경우이 연산자는 None (입력 변경 없음)으로 평가됩니다. 그래도 옵션이 Some x이면 연산자는 오른쪽에있는 함수를 x에 적용합니다. 운영자가 이미 옵션 (없음)을 잠재적으로 평가하고 있으므로 분명히이 기능도이를 수행해야합니다. 이 연산자를 사용하면 None을 변경하지 않고 전달하거나 옵션의 내용을 조작 할 수있는 함수를 쉽게 사용하고 연쇄 할 수 있습니다. 재고없이 또는 out 슬롯없이 블록을 감안할 때

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) 

, add_item block 반환이 변경되지 않은 : 리드

. 인벤토리가있는 블록이 있고 out 슬롯에있는 것이 있으면 add_item block 카운트가 증가합니다.

그러나, 코멘트에 당신은 문제가 야해 [없음에 오류에 제기 즉 실제로

말 때문에 그렇지 않은 경우에만 블록으로 호출되는 함수입니다.

즉 코드의 일부가 실제로 옵션을 다루지 않습니다.유형이 현실과 더 이상 일치하지 않으며 유형 검사기의 안전성을 포기합니다. 즉, 컴파일러가 코드를 통해 실행되지 않을 것이므로 런타임에 오류를 발생시키지 않습니다. 타입 시스템이지만, 당신은 철저히 논리를 점검했기 때문에. 나중에 그 논리에 버그가 생기면 컴파일러는 경고 할 수 없습니다. 귀하의 유일한 통지는 놀라운 런타임 오류입니다.

+1

이 방법을 (>> =) 정의하는 것이 좀 더 실용적 일 것이라고 생각합니다. let (>> =) o f = | 없음 -> 없음 | 일부 x -> 일부 (f x)''''. 그것은 연산자의 정의 안에 일부를 추가하고 다른 함수에서 그것을 제거 할 수있게 해준다. – ghilesZ

+0

@ghilesZ이 정의는 종종'map'이라고 불리며'Some '없음 '으로 설정합니다. –

관련 문제