할당없이 상태 유지


10

함수형 프로그래밍을 배우고 있으며 할당을 사용하지 않고 특정 시나리오를 구현하는 방법을 이해하는 데 어려움이 있습니다. 다음의 간단한 문제는 혼란을 요약합니다.

주어진 데이터 구조의 변경에 대한 이벤트를 수신하고이 데이터 구조가 특정 상태에 도달하면 이벤트를 생성하는 프로그램을 작성하십시오.

그래서 내가 유지하는 데이터 구조의 사본이 있습니다.

datastructure_copy::DataStructure 

변경되면 시작되는 이벤트 스트림이 있습니다.

datastructure_changes::Stream Change

데이터 구조에 변경 사항을 적용하고 새 복사본을 반환하는 함수가 있습니다.

apply_change::Change -> DataStructure -> DataStructure

그리고 데이터 상태가 원하는 상태에 도달했는지 확인하는 조건자가 있습니다.

is_ready::DataStructure ->Boolean

다시 말해 스트림에서 작동하는 '감소'와 같은 것이 필요합니다.

나는 이것을 구현하는 한 가지 방법은 변화가 도착할 때마다 상태를 재 계산하는 것임을 알고 있지만, 이것은 비현실적 인 것 같습니다. State 모나드와 약간 연주했지만 다른 문제를 해결하기위한 것 같습니다.

그래서 다른 방법이 있습니까?

내 질문은 순전히 개념이며 Haskell에 대해서는 잘 알고 있지 않습니다.


어떤 식 으로든 하스켈에서는 '할당', '변경 가능 상태'또는 '변수'라는 개념이없고 "순수" 이기 때문에 하스켈에서는 '할당'(할당과 바인딩의 차이점에 유의)을 볼 수 없습니다. 기능 언어 " ' . State Monad는 당신이 찾고있는 것이어야합니다. 사용 방법을 배우기 만하면됩니다. 오늘 후반에보다 포괄적 인 답변을 드릴 수 있습니다.
프란체스코 그라 마노

답변:


2

나는 이것을 구현하는 한 가지 방법은 변화가 도착할 때마다 상태를 재 계산하는 것임을 알고 있지만, 이것은 비현실적 인 것 같습니다.

이벤트가 발생할 때 적용된 변경 사항이 어떤 방식 으로든 분산되지 않는 경우 최종 상태는 초기 상태에 지나지 않고 연속적인 변경 사항이므로 이벤트가 발생할 때마다 상태를 다시 계산해야합니다. 그리고 변경 사항이 분산 적이라하더라도 주어진 상태에 도달하는 한 빨리 프로세스를 중지하고 다음 상태를 계산하여 다음 상태를 결정해야하므로 일반적으로 상태를 다음 상태로 연속 변환하려고합니다. 새로운 것은 원하는 상태입니다.

함수형 프로그래밍에서 상태 변경은 일반적으로 함수 호출 및 / 또는 함수 매개 변수로 표시됩니다.

최종 상태가 언제 계산되는지 예측할 수 없으므로 비 꼬리 재귀 함수를 사용하지 않아야합니다. 각 상태가 이전 상태를 기반으로하는 상태 스트림이 좋은 대안이 될 수 있습니다.

따라서 귀하의 경우 스칼라에서 다음 코드로 질문에 대답합니다.

import scala.util.Random

val initState = 0.0
def nextState(state: Double, event: Boolean): Double = if(event) state + 0.3 else state - 0.1 // give a new state
def predicate(state: Double) = state >= 1

// random booleans as events
// nb: must be a function in order to force Random.nextBoolean to be called for each  element of the stream
def events(): Stream[Boolean] = Random.nextBoolean #:: events()  

val states: Stream[Double] = initState #:: states.zip(events).map({ case (s,e) => nextState(s,e)}) // a stream of all the successive states

// stop when the state is >= 1 ;
// display all the states computed before it stopped
states takeWhile(! predicate(_)) foreach println 

예를 들어 어떤 결과를 줄 수 있습니까?

0.0
0.3
0.2
0.5
0.8

val states: Stream[Double] = ... 연속적인 상태가 계산되는 라인입니다.

이 스트림의 첫 번째 요소는 시스템의 초기 상태입니다. zip상태 스트림을 이벤트 스트림과 요소 쌍의 단일 스트림으로 병합합니다. 각 쌍은 (상태, 이벤트)입니다. map각 쌍을 새 상태 인 단일 값으로 변환하고 이전 상태 및 관련 이벤트의 함수로 계산합니다. 따라서 새로운 상태는 이전에 계산 된 상태와 상태를 "수정"하는 관련 이벤트입니다.

따라서 기본적으로 잠재적으로 무한한 상태 스트림을 정의합니다. 각 새로운 상태는 마지막 계산 된 상태의 함수이며 새로운 이벤트입니다. 스칼라에서는 스트림이 느리기 때문에 (주문형으로 만) 계산되므로 필요없는 상태를 계산할 필요가 없으며 원하는 수의 상태를 계산할 수 있습니다.

술어를 존중하는 첫 번째 상태에만 관심이있는 경우 마지막 코드 행을 다음으로 바꾸십시오.

states find predicate get

어떤 검색 :

res7: Double = 1.1

마법을 행하는 라인에 대한 통찰력을 제공 할 수 있습니까?val states: Stream[Double]...
Bobby Marinoff

확실한. 내 편집 내용을보십시오.
mgoeminne

1

두 가지 기능이 있다고 말합니다.

apply_change::Change -> DataStructure -> DataStructure
is_ready::DataStructure ->Boolean

내가 당신을 올바르게 이해한다면 is_ready오히려 비싸므로 모든 변경 이벤트에 대해 반복해서 그것을하고 싶지 않습니다.

필요한 것은 함수가 초기 DataStructure를 취해이를 간단한 상태로 압축하고 압축 된 상태 인 Change를 가져 와서 새로운 압축 된 상태를 출력하는 함수입니다.

DataStructure가 삼중 항 x,y,z이고 x, y 및 z가 소수가되기를 기다리고 있다고 가정하십시오 . 그러면 압축 상태는 x, y, z 중 소수가 아닐 수 있습니다. x 소수를 변경하면 세트에서 x가 제거됩니다. x가 소수가 아닌 변경은 x를 세트에 추가합니다 (없는 경우). DataStructure가 준비된 후 세트가 비어 있습니다.

압축 된 상태를 업데이트하는 것이 DataStructure를 업데이트하는 것보다 훨씬 저렴하고 처음부터 컴퓨팅을 준비하는 것이 좋습니다.

참고 : 더 나은 방법은 x, y, z 중 어느 것이 소수인지 확인하고 어디에 있는지 추적하는 것입니다. 모든 변경에 대해 관련 필드를 선택하지 않은 것으로 표시하십시오. 그런 다음 is_ready가 호출되면 확인하고 기억하십시오. x가 여러 번 변경 될 수 있고 소수를 한 번만 확인하기 때문에 모든 변경 후에 is_ready를 확인하지 않는 것이 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.