[scala] 함수형 프로그래밍에서 시간 함수는 어떻게 존재할 수 있습니까?

함수형 프로그래밍에 대해 잘 모른다는 것을 인정했습니다. 나는 여기저기서 그것에 대해 읽었고 함수 프로그래밍에서 함수는 호출 횟수에 관계없이 동일한 입력에 대해 동일한 출력을 반환한다는 것을 알게되었습니다. 함수 표현식과 관련된 입력 매개 변수의 동일한 값에 대해 동일한 출력으로 평가하는 수학 함수와 정확히 같습니다.

예를 들어 다음을 고려하십시오.

f(x,y) = x*x + y; // It is a mathematical function

몇 번 사용하든 f(10,4)그 가치는 항상 그렇습니다 104. 따라서, 당신이 작성한 곳마다 전체 표현식의 값을 변경하지 않고 f(10,4)그것을로 바꿀 수 있습니다 104. 이 속성을 표현식의 참조 투명성 이라고합니다 .

Wikipedia에 따르면 ( link )

반대로 함수 코드에서 함수의 출력 값은 함수에 입력 된 인수에만 의존하므로 인수 x에 대해 동일한 값으로 함수 f를 두 번 호출하면 동일한 결과 f (x)가 두 번 생성됩니다.

함수형 프로그래밍에 현재 시간 을 반환하는 시간 함수 가 존재할 수 있습니까?

  • 그렇다면 어떻게 존재할 수 있습니까? 함수형 프로그래밍 원칙을 위반하지 않습니까? 특히 함수 프로그래밍의 속성 중 하나 인 참조 투명성 을 위반 합니다 (정확하게 이해하는 경우).

  • 아니면 기능 프로그래밍에서 현재 시간을 어떻게 알 수 있습니까?



답변

그것을 설명하는 또 다른 방법은 이것입니다 : 어떤 함수도 현재 시간을 얻을 수는 없지만 (계속 변경되기 때문에) 액션 은 현재 시간을 얻을 수 있습니다. 이것이 현재 시간을 얻는 동작getClockTime 을 나타내는 상수 (또는 원하는 경우 null 함수) 라고 가정 해 봅시다 . 이 동작 은 언제 사용 되든 항상 동일하므로 실제 상수입니다.

마찬가지로 print시간 표현을 콘솔에 출력하는 기능을 가정 해 봅시다 . 함수 호출은 순수한 기능 언어로 부작용을 가질 수 없으므로 대신 타임 스탬프 를 사용하여 콘솔로 인쇄 하는 동작 을 반환하는 함수라고 상상해보십시오 . 다시 말하지만, 동일한 타임 스탬프를 부여 하면 매번 동일한 인쇄 작업 을 반환하므로 실제 함수 입니다.

이제 콘솔에 현재 시간을 어떻게 인쇄 할 수 있습니까? 글쎄, 당신은 두 행동을 결합해야합니다. 어떻게 그렇게 할 수 있습니까? 우리는 전달할 수 없습니다 getClockTimeprint인쇄가 타임 스탬프 아닌 행동을 기대하고 있기 때문에. 그러나 우리 두 개의 동작 >>=결합 하는 연산자가 있다고 생각할 수 있습니다 . 하나는 타임 스탬프를 얻는 것과 다른 하나는 인수로 취하여 그것을 인쇄합니다. 이것을 이전에 언급 한 액션에 적용하면 결과는 … tadaaa … 현재 시간을 가져 와서 인쇄하는 새로운 액션입니다. 그리고 이것은 우연히 Haskell에서 어떻게 수행되는지입니다.

Prelude> System.Time.getClockTime >>= print
Fri Sep  2 01:13:23 東京 (標準時) 2011

따라서 개념적으로는 다음과 같이 볼 수 있습니다. 순수 기능 프로그램은 I / O를 수행하지 않고 런타임 시스템이 실행 하는 action을 정의합니다 . 동작은 매번 동일하지만 그것의 실행 결과가 실행되었을 때의 상황에 따라 달라진다.

이것이 다른 설명보다 분명한지 모르겠지만 때로는 이런 식으로 생각하는 데 도움이됩니다.


답변

예, 아니오

다른 기능적 프로그래밍 언어는 다르게 해결합니다.

하스켈 (매우 순수 하나)에서 모든 물건을 호출 뭔가 발생하는 I / O 모나드 참조 – 여기 .

함수 (세계 상태)에 다른 입력 (및 출력)을 가져 오는 것으로 생각하거나 변화하는 시간을 얻는 것과 같은 “불순”이 발생하는 장소로 쉽게 생각할 수 있습니다.

F #과 같은 다른 언어에는 불완전 성이 내장되어 있으므로 일반적인 명령 언어 와 마찬가지로 동일한 입력에 대해 다른 값을 반환하는 함수를 가질 수 있습니다.

Jeffrey Burka가 그의 의견에서 언급 한 것처럼 : Haskell 위키에서 I / O Monad에 대한 좋은 소개가 있습니다.


답변

Haskell에서는 모나드 (monad) 라는 구문을 사용하여 부작용을 처리합니다. 모나드는 기본적으로 값을 컨테이너에 캡슐화하고 함수를 값에서 컨테이너 내부의 값으로 연결하는 함수를 가지고 있음을 의미합니다. 컨테이너 유형이 다음과 같은 경우 :

data IO a = IO (RealWorld -> (a,RealWorld))

IO 액션을 안전하게 구현할 수 있습니다. 이 유형은 다음을 의미합니다. 유형의 조치는 유형 IO의 토큰을 가져 와서 RealWorld결과와 함께 새 토큰을 리턴하는 함수입니다.

이에 대한 아이디어는 각 IO 동작이 마법 토큰으로 표시되는 외부 상태를 변경한다는 것 RealWorld입니다. 모나드를 사용하면 현실 세계를 함께 변화시키는 여러 기능을 연결할 수 있습니다. 모나드의 가장 중요한 기능입니다 >>=, 발음 바인딩 :

(>>=) :: IO a -> (a -> IO b) -> IO b

>>=하나의 조치와이 조치의 결과를 가져 와서 새 조치를 작성하는 함수를 취합니다. 리턴 유형은 새로운 조치입니다. 예를 들어 now :: IO String현재 시간을 나타내는 문자열을 반환하는 function이 있다고 가정 해 봅시다 . putStrLn인쇄 기능과 연결 하여 인쇄 할 수 있습니다.

now >>= putStrLn

또는 do명령형 프로그래머에게 더 친숙한 -Notation으로 작성하십시오 .

do currTime <- now
   putStrLn currTime

우리가 외부 세계에 대한 돌연변이와 정보를 RealWorld토큰 에 매핑 할 때이 모든 것이 순수합니다 . 따라서이 작업을 실행할 때마다 물론 다른 출력을 얻지 만 입력은 동일하지 않습니다 RealWorld. 토큰이 다릅니다.


답변

대부분의 함수형 프로그래밍 언어는 순수하지 않습니다. 즉, 함수가 값에 의존 할뿐만 아니라 함수도 허용합니다. 이러한 언어에서는 현재 시간을 반환하는 함수를 완벽하게 사용할 수 있습니다. 이 질문에 태그를 추가 한 언어에서 ScalaF # ( ML의 대부분의 다른 변형 )에 적용됩니다.

순수한 HaskellClean 언어에서는 상황이 다릅니다. Haskell에서는 현재 시간을 함수를 통해 사용할 수 없지만, 소위 IO 액션이라고하며 Haskell의 부작용을 캡슐화하는 방법입니다.

Clean에서는 함수이지만 함수는 월드 값을 인수로 사용하고 결과로 현재 시간 외에 새로운 월드 값을 반환합니다. 타입 시스템은 각 월드 값을 한 번만 사용할 수있게합니다 (그리고 월드 값을 소비하는 각 함수는 새로운 값을 생성합니다). 이렇게하면 매번 다른 인수로 시간 함수를 호출해야하므로 매번 다른 시간을 반환 할 수 있습니다.


답변

“현재 시간”은 기능이 아닙니다. 매개 변수입니다. 코드가 현재 시간에 의존하는 경우 코드가 시간별로 매개 변수화됨을 의미합니다.


답변

순전히 기능적인 방식으로 수행 할 수 있습니다. 여러 가지 방법이 있지만 가장 간단한 방법은 시간 함수가 시간뿐만 아니라 다음에 측정 할 때 호출해야하는 함수를 반환하는 것 입니다.

C #에서는 다음과 같이 구현할 수 있습니다.

// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
    public static readonly ClockStamp ProgramStartTime = new ClockStamp();
    public readonly DateTime Time;
    private ClockStamp _next;

    private ClockStamp() {
        this.Time = DateTime.Now;
    }
    public ClockStamp NextMeasurement() {
        if (this._next == null) this._next = new ClockStamp();
        return this._next;
    }
}

(이것은 실용적이지 않고 단순하도록 의도 된 예입니다. 특히, 목록 노드는 ProgramStartTime에 의해 시작되므로 가비지 수집 될 수 없습니다.)

이 ‘ClockStamp’클래스는 변경할 수없는 링크 된 목록처럼 작동하지만 실제로 노드는 요청시 생성되므로 ‘현재’시간을 포함 할 수 있습니다. 시간을 측정하려는 모든 함수는 ‘clockStamp’매개 변수를 가져야하며 결과에서 마지막 시간 측정 값을 반환해야합니다 (따라서 호출자에게 이전 측정 값이 표시되지 않음).

// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
    public readonly ClockStamp Time;
    public readonly T Value;
    public TimeStampedValue(ClockStamp time, T value) {
        this.Time = time;
        this.Value = value;
    }
}

// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
    var start = lastMeasurement.NextMeasurement();
    for (var i = 0; i < 10000000; i++) {
    }
    var end = start.NextMeasurement();
    var duration = end.Time - start.Time;
    return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
    var clock = ClockStamp.ProgramStartTime;
    var r = TimeALoop(clock);
    var duration = r.Value; //the result
    clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}

물론, 마지막 측정을 안팎으로, 안팎으로 통과시키는 것이 약간 불편합니다. 보일러 플레이트를 숨기는 방법에는 여러 가지가 있는데, 특히 언어 디자인 수준에서 그렇습니다. Haskell은 이런 종류의 트릭을 사용하고 모나드를 사용하여 못생긴 부분을 숨기고 있다고 생각합니다.


답변

나는 대답이나 의견 중 어느 것도 대수 또는 공동화에 대해 언급하지 않은 것에 놀랐습니다. 일반적으로 무한 데이터 구조에 대해 추론 할 때 공동화가 언급되지만 CPU의 시간 레지스터와 같은 끝없는 관측 스트림에도 적용됩니다. 대수는 숨겨진 상태를 모델링합니다. 그리고 그 상태를 관찰 하는 공동화 모델 . (일반 유도 모델 구축 상태를 )

이것은 Reactive Functional Programming의 주요 주제입니다. 이런 종류의 것들에 관심이 있다면 이것을 읽으십시오 : http://digitalcommons.ohsu.edu/csetech/91/ (28 pp.)