기능적 프로그래밍 및 텍스트 어드벤처


14

이것은 주로 FP에 대한 이론적 인 질문이지만, 내 요점을 설명하기 위해 구식 Zork과 같은 텍스트 모험을 할 것입니다. FP를 사용하여 상태 기반 시뮬레이션을 모델링하는 방법에 대한 귀하의 의견을 알고 싶습니다.

텍스트 어드벤처는 실제로 OOP를 요구하는 것 같습니다. 예를 들어, 모든 "방"은 Room클래스의 인스턴스이며, 휴대 할 수있는 Item것과 같은 기본 클래스 및 인터페이스 Item<Pickable>를 가질 수 있습니다.

FP의 월드 모델링은 다른 방식으로 작동합니다. 특히 게임이 진행될 때 (객체가 이동하거나 적을 물리 치고 점수가 올라가고 플레이어의 위치가 변경됨) 변경 해야하는 월드에서 불변성을 적용하려는 경우에 특히 그렇습니다 . 나는 World당신이 탐험 할 수있는 방은 무엇이고, 어떻게 연결되어 있는지, 플레이어가 가지고 다니는 것, 어떤 레버가 작동했는지를 모두 갖춘 하나의 큰 물체 를 상상합니다 .

순수한 접근 방식은 기본적 으로이 큰 객체를 모든 함수에 전달하고 반환 할 수 있다고 생각합니다 (아마도 수정). 예를 들어 새 방 으로 변경하여 moveToRoom가져 오는 함수 등이 있습니다.WorldWorld.player.locationWorld.rooms[new_room].visited = True

이것이 더 "올바른"방법이더라도 그것을 위해 순결을 강화하는 것 같습니다. 프로그래밍 언어에 따라이 잠재적으로 매우 큰 World객체를 앞뒤로 전달하면 비용이 많이들 수 있습니다. 또한 모든 기능은 모든 World객체에 액세스해야 할 수도 있습니다 . 예를 들어, 방이 침수 될 수 있기 때문에 다른 방에서 트리거 된 레버에 따라 방에 접근 할 수 있거나 접근하지 않을 수 있지만 플레이어가 구명 조끼를 가지고 있다면 어쨌든 들어갈 수 있습니다. 플레이어가 다른 방에서 사촌을 학살했는지 여부에 따라 몬스터가 공격적이거나 아닐 수 있습니다. 것을이 수단 roomCanBeEntered기능에 액세스해야 World.player.invetory하고 World.rooms, describeMonster액세스해야하는 World.monsters등 기본적으로, 당신은 (에 해야전체 하중을 전달하십시오). 이것은 이것이 FP에서 특히 좋은 프로그래밍 스타일이지만 글로벌 변수를 요구하는 것 같습니다.

이 문제를 어떻게 해결 하시겠습니까?


4
"프로그래밍 언어에 따라,이 잠재적으로 매우 큰 World 오브젝트를 앞뒤로 전달하는 것은 비용이 많이들 수 있습니다." 아마도 참조로 전달 될 것입니다. "또한 모든 기능은 모든 월드 오브젝트에 액세스해야 할 수도 있습니다." 모든 기능 이 게임 의 전체 상태에 액세스해야 한다고 믿기가 어렵습니다 .
Doval

2
Chris Marten의 연구는 흥미로울 것입니다. 선언적 언어로 대화 형 소설을 멋지게 만드는 방법을 보여줍니다. github.com/chrisamaphone/interactive-lp
Daniel Gratzer

2
이러한 게임을 기능적으로 프로그래밍하는 저자의 접근 방식을 설명하는 이 블로그 를 살펴볼 수 있습니다 . 이 게시물 은 특히 ​​중요합니다.
gallais

3
이 질문이 @EricLippert의 나중에 Ocaml에서 Z 머신을 구현하는 것에 대한 일련의 기사를 작성하기로 한 결정에 영향을 미쳤는지 궁금합니다.
Jules

1
@Jules 관심있는 분들을위한 시리즈 시작의 링크 : ericlippert.com/2016/02/01/west-of-house
KChaloux

답변:


4

함수형 언어는 객체 대신 데이터 구조와 분리 된 함수를 사용합니다. 예를 들어, 방 세트와 인벤토리 항목 목록을 대신 월드로 만들 수 있습니다.

이상적으로는 전 세계를 전달하는 대신 함수에 제공하는 데이터의 양을 가능한 한 실제로 필요한 양으로 제한 할 것입니다 (예를 들어, 세계에서 하나의 관련 방을 추출하는 것은 물론 완전히 상호 의존적 인 세계는 어려울 수 있습니다) 갈라진). 결과는 세계 데이터 구조로 다시 통합되어 새로운 상태를 만듭니다. 상태를 사용하지 않으면 상태를 모델링 할 수 없습니다. 당신이 말하는 것처럼 본질적으로 돌연변이가 필요한 것들이 있습니다.

대부분의 실용적인 기능적 언어는 직접적으로 (Haskell의 ST 모나드 또는 Clojure의 과도 현상) 돌연변이를 실현하거나 효율적으로 시뮬레이션하는 방법을 제공합니다 (종종 데이터 구조의 변경되지 않은 부분을 재사용함으로써 (Clojure의 기본 데이터 구조는 여기에서 좋은 예입니다)) ). 어느 쪽이든 순도가 유지됩니다.

변경해야 할 상태의 양이 제한되어 있기 때문에 성능 문제에 대해 너무 걱정하지 않고 (아마도 최적화 된) 순진한 기능적 접근 방식을 사용합니다.

내가 본 다른 옵션은 세계의 일부를 개별 기능에서 변경하라는 지시 만 반환 한 다음 이에 따라 세계를 업데이트하는 것입니다. 이를 설명하는 일련의 블로그 게시물은 http://prog21.dadgum.com/23.html 에서 볼 수 있습니다 .

이 두 가지 답변은 전 세계를 기능에 전달하지 않는 것보다 변경 사항을 구성하는 방법에 대해 더 많이 다루고 있습니다. 왜냐하면 완벽하게 상호 의존적 인 정의는 정의로 분류 할 수 없기 때문에 최선을 다해 시도하십시오. 기능적이지만 좋은 습관입니다.


0

본인은 문제의 언어가 어떤 형태의 데이터베이스에 액세스 할 수 있는지 확실히 조사 할 것입니다. 세계의 상태를 동시에 변경하는 대부분의 이벤트는 단순히 디스크에 기록되며 현재 방 (폭발과 같은 특수한 상황 또는 문을 여는 MMO 제외) 내 현재 플레이어에 영향을 미치지 않습니다. 원격으로 다른 플레이어의 소리 등).

따라서 현재 클라이언트는 Room객체와 객체에 직접 영향을 미치는 사항 만 인식 하면됩니다. noticableEventsOutsideRoom그러면 Room데이터베이스의 최근 변경 사항 에 영향 을 받는 하위 클래스 가 될 수 있으며 게임 개체가 훨씬 작아졌습니다.


이 방법은 로컬 이벤트 (예 : 근처 몹의 농업과 같은)를 길 찾기 또는 트리거하는 데 많은 도움이되지 않는다는 것을 알고 있지만 과거에는 데이터베이스를 악용하는 것으로 알려져 update mobs set agro=1 where distance<5있습니다. 그것으로 끝났다. 어쩌면 그것은 최선의 방법은 아니지만 내 목적에 적합합니다. 데이터베이스를 통한 길 찾기는 항상 Dijkstra의 최단 경로 알고리즘을 사용할 수 있습니다 .
Ayelis

0

진짜 해결책은 되지 그것을 주변에 통과 한 후 큰 세계 객체에 모든 것을 수집에 의해. 대신 다루는 함수의 유형을 정확하게 지정하는 것이 좋습니다. 몇 가지 예는 다음과 같습니다.

BAD:
   f :: (World, Int) -> World

Good:
   f :: (Int,Int,Int,Int) -> World

나쁜 예는 기존 개체를 수정하려고하지만 좋은 예는 게임의 상태 공간에서 월드를 만들려고하는 것입니다. 기본적으로 세계를 만들려면 올바른 세계를 선택하는 데 필요한 모든 데이터 를 알아야 합니다 .

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