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


646

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

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

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)가 두 번 생성됩니다.

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

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

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


15
나는 대부분의 (또는 모든) 기능적 언어가 그렇게 엄격하지 않고 기능적 및 명령 적 프로그래밍을 결합한다고 생각합니다. 적어도 이것은 F #에서 얻은 인상입니다.
Alex F

13
@Adam : 발신자가 처음부터 현재 시간을 어떻게 알 수 있습니까?
Nawaz

29
@ 아담 : 실제로는 순수하게 기능적인 언어에서는 (불법으로) 불법입니다.
sepp2k

47
@ 아담 : 거의. 순수한 범용 언어는 일반적으로 참조 투명성을 손상시키지 않고 "세계 상태"(즉, 현재 시간, 디렉토리의 파일 등)에 도달 할 수있는 기능을 제공합니다. Haskell에서는 이것이 IO 모나드이고 Clean에서는 세계 유형입니다. 따라서 해당 언어에서 현재 시간이 필요한 함수는 인수로 사용하거나 실제 결과 (Haskell) 대신 IO 동작을 반환하거나 월드 상태를 인수 (Clean)로 가져와야합니다.
sepp2k

12
FP에 대해 생각할 때 잊어 버리기 쉽습니다. 컴퓨터는 변경 가능한 상태입니다. FP는 그것을 바꾸지 않고 단지 감추고 있습니다.
다니엘

답변:


176

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

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

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

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

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

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


33
설득력이 없습니다. getClockTime함수 대신 작업을 편리하게 호출 했습니다. 음, 호출하면 모든 함수 action 을 호출하면 명령형 프로그래밍조차도 함수형 프로그래밍이됩니다. 아니면 그것을 행동적인 프로그래밍 이라고 부르고 싶을 수도 있습니다.
Nawaz

92
@Nawaz : 여기서 주목할 점은 함수 내에서 작업을 실행할 수 없다는 것입니다. 액션과 기능을 결합하여 새로운 액션을 만들 수 있습니다. 조치를 실행하는 유일한 방법은 조치를 조치로 작성하는 것 main입니다. 따라서 순수 기능 코드를 명령형 코드와 분리 할 수 ​​있으며 이러한 분리는 유형 시스템에 의해 시행됩니다. 액션을 일류 객체로 취급하면 객체를 전달하고 자신 만의 "제어 구조"를 만들 수 있습니다.
hammar

36
하스켈의 모든 기능이 기능은 아닙니다. 함수는 유형에 a가 포함 된 것입니다. ->즉, 표준이 용어를 정의하는 방식이며 실제로 Haskell의 맥락에서 유일하게 현명한 정의입니다. 따라서 유형이 함수 IO Whatever아닌 것이 있습니다.
sepp2k

9
@ sepp2k 그래서, myList :: [a-> b]는 함수입니까? ;)
fuz September

8
@ThomasEding 나는 정말로 파티에 늦었지만, 나는 이것을 명확히하고 싶다 : putStrLn행동이 아니라 행동을 반환 하는 함수이다 . 조치 getLine포함 하는 변수입니다 . 행동은 가치, 변수 및 기능은 우리가 그 행동을주는 "컨테이너"/ "라벨"입니다.
kqr

356

예, 아니오

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

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

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

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

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


223
Haskell의 IO 모나드에 대해 알아야 할 중요한 사항은이 문제를 해결하기위한 해킹이 아니라는 것입니다. 모나드는 어떤 맥락에서 일련의 동작을 정의하는 문제에 대한 일반적인 해결책입니다. 하나의 가능한 맥락은 우리가 IO 모나드를 가지고있는 실제 세계입니다. 또 다른 컨텍스트는 원자 트랜잭션 내에 있으며 STM 모나드가 있습니다. 또 다른 맥락은 우리가 ST 모나드를 갖는 순수한 함수로서 절차 적 알고리즘 (예 : 크 누스 셔플)을 구현하는 것이다. 자신 만의 모나드도 정의 할 수 있습니다. Monad는 일종의 오버로드 가능한 세미콜론입니다.
Paul Johnson

2
현재 시간을 "기능"으로 얻는 것과는 달리 "프로 시저"와 같은 것을 호출하지 않는 것이 유용하다는 것을 알았습니다 (하스켈 솔루션은 예외입니다).
singpolyma

Haskell의 관점에서 고전적인 "프로 시저"( '...-> ()'와 같은 유형을 가진 것)는 ...-> ()를 가진 순수한 함수로서 다소 사소합니다.
Carsten

3
전형적인 Haskell 용어는 "action"입니다.
Sebastian Redl

6
"모나드는 일종의 오버로드 가능한 세미콜론입니다." +1
2805751

147

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. 토큰이 다릅니다.


3
-1 : RealWorld연기 화면이 마음에 들지 않습니다 . 그러나 가장 중요한 것은이 목표 된 객체가 체인으로 전달되는 방식입니다. 누락 된 부분은 그것이 시작되는 곳, 실제 세계에 대한 소스 또는 연결이있는 곳-IO 모나드에서 실행되는 주요 기능으로 시작합니다.
u0b34a0f6ae

2
@ kaizer.se RealWorld프로그램이 시작될 때 프로그램에 전달 되는 전역 객체를 생각할 수 있습니다 .
fuz

6
기본적으로 main함수는 RealWorld인수를 취합니다 . 사형 집행이있을 때만 통과됩니다.
Louis Wasserman

13
알다시피, 그들이 숨기고 RealWorld펑키 기능 만 제공 하는 이유 putStrLn는 일부 Haskell 프로그래머가 RealWorldHaskell Curry의 주소와 생년월일이 옆집 이웃이되도록 프로그램 중 하나를 사용하여 변경하지 않기 때문입니다.
자라서 (

2
RealWorld -> (a, RealWorld) 우주의 다른 부분이 당신의 기능 (또는 현재 프로세스) 외부에서 항상 변경 될 수 있다는 것을 명심하는 한 동시성에서도 은유로 분해되지 않습니다. 따라서 (a) 메타 포어가 분해되지 않으며 (b) RealWorld유형이 있는 값이 함수에 전달 될 때마다 실제 세계 그 동안 변경되었으므로 함수를 다시 평가 해야합니다 ( @fuz가 설명 한 것처럼 모델링되어 실제 세계와 상호 작용할 때마다 다른 '토큰 가치'를 돌려줍니다).
Qqwy

73

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

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

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


2
마치 Haskell과 Clean이 다른 일을하는 것처럼 들립니다. 내가 이해 한 바에 따르면, 그들은 Haskell이 이것을 달성하기 위해 더 좋은 구문 (?)을 제공한다는 것만 똑같이합니다.
Konrad Rudolph

27
@ Konrad : 그들은 유형 시스템 기능을 사용하여 부작용을 추상화한다는 점에서 동일한 작업을 수행하지만 그에 관한 것입니다. IO 유형을 월드 유형으로 설명하는 것은 매우 좋지만 Haskell 표준은 실제로 월드 유형을 정의하지 않으며 Haskell에서 World 유형의 값을 실제로 얻을 수는 없습니다 (매우 가능하고 실제로는 가능합니다) 깨끗해야 함). 추가 Haskell에는 유형 시스템 기능으로 고유 한 타이핑이 없기 때문에 World에 액세스 할 수 있다면 Clean이하는 방식대로 순수한 방식으로 사용할 수 없습니다.
sepp2k

51

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


22

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

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은 이런 종류의 트릭을 사용하고 모나드를 사용하여 못생긴 부분을 숨기고 있다고 생각합니다.



@ snim2 완벽하지 않습니다. : P 더티 가변성이 결과의 참조 투명도에 영향을 미치지 않는다는 사실에 위안을 둡니다. 동일한 'lastMeasurement'를 두 번 통과하면 다음 측정 값이 오래되고 동일한 결과가 반환됩니다.
Craig Gidney 2016 년

@Strilanc 감사합니다. 나는 명령형 코드로 생각하기 때문에 기능적 개념이 이런 식으로 설명되는 것을 보는 것이 흥미 롭습니다. 자연스럽고 구문 적으로 더 깨끗한 언어를 상상할 수 있습니다.
WW.

실제로 C #에서도 모나드 방식으로 갈 수 있으므로 명시 적으로 타임 스탬프가 전달되는 것을 피할 수 있습니다. 같은 것이 필요합니다 struct TimeKleisli<Arg, Res> { private delegate Res(TimeStampedValue<Arg>); }. 그러나이 코드는 여전히 Haskell만큼 do구문이 좋지 않습니다 .
leftaroundabout

@leftaroundabout 당신은 SelectManyquery comprehension 구문을 가능하게 하는 메소드로 bind 함수를 구현함으로써 C #에 모나드를 가지고있는 것처럼 보일 수있다 . 그래도 모나드에 대해 다형성으로 프로그래밍 할 수는 없으므로 약한 유형 시스템과의 모든 오르막 전투입니다. (
sara

16

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

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


3
그리고이 질문과 어떻게 관련이 있습니까?
Nawaz

5
귀하의 질문은 현재 시스템 시계를 반환하는 함수와 같이 순전히 기능적인 방식으로 시간 종속 동작을 모델링하는 것입니다. 모든 기능과 종속성 트리를 통해 IO 모나드와 동등한 것을 스레드하여 해당 상태에 액세스 할 수 있습니다. 또는 구성 규칙이 아닌 관찰 규칙을 정의하여 상태를 모델링 할 수 있습니다. 그렇기 때문에 함수형 프로그래밍에서 복잡한 상태를 귀납적 으로 모델링하는 것은 부자연 스러워 보입니다 . 숨겨진 상태는 실제로 일치하는 속성 이기 때문 입니다.
Jeffrey Aguilera

훌륭한 소스! 더 최근의 것이 있습니까? JS 커뮤니티는 여전히 스트림 데이터 추상화로 어려움을 겪고 있습니다.
Dmitri Zaitsev

12

예, 순수 함수가 해당 시간을 매개 변수로 제공 한 경우 시간을 리턴 할 수 있습니다. 다른 시간 논쟁, 다른 시간 결과. 그런 다음 다른 시간 함수도 구성하고 간단한 함수 어휘 변환 기능 (고차) 함수와 결합하십시오. 접근 방식은 상태 비 저장이므로 여기에서 시간은 분리형이 아닌 연속적 (해상도 독립적) 일 수 있으며 모듈성을 크게 향상 시킵니다. 이 직관은 FRP (Functional Reactive Programming)의 기초입니다.


11

예! 당신이 올바른지! Now () 또는 CurrentTime () 또는 이러한 특징의 메소드 서명은 어떤 방식 으로든 참조 투명성을 나타내지 않습니다. 그러나 컴파일러의 지시에 따라 시스템 클록 입력에 의해 매개 변수화됩니다.

결과적으로 Now ()는 참조 투명도를 따르지 않는 것처럼 보일 수 있습니다. 그러나 시스템 클럭의 실제 동작과 그 위에있는 기능은 참조 투명성을 준수합니다.


11

예, 불완전한 기능 프로그래밍 (기본 또는 기본 기능은 순수 기능 프로그래밍)으로 알려진 기능 프로그래밍에서 약간 수정 된 버전을 사용하여 기능 프로그래밍에 시간이 걸리는 기능이 존재할 수 있습니다.

시간을 얻거나 파일을 읽거나 미사일을 발사하는 경우 코드는 외부 세계와 상호 작용하여 작업을 완료해야하며이 외부 세계는 함수형 프로그래밍의 순수한 기초를 기반으로하지 않습니다. 순수한 기능적 프로그래밍 세계가이 불순한 외부 세계와 상호 작용할 수 있도록 사람들은 불완전한 기능적 프로그래밍을 도입했습니다. 결국 외부 세계와 상호 작용하지 않는 소프트웨어는 수학 계산을 수행하는 것 외에는 유용하지 않습니다.

어떤 기능적 프로그래밍 프로그래밍 언어에는 이러한 불순물 기능이 내장되어있어 어떤 코드가 불완전하고 어떤 코드가 순수한지 (F # 등) 쉽게 구분할 수 없으며 일부 기능적 프로그래밍 언어는 불완전한 작업을 수행 할 때 이 코드는 Haskell과 같은 순수한 코드와 비교할 때 분명히 두드러집니다.

이것을 보는 또 다른 흥미로운 방법은 함수형 프로그래밍에서 시간 함수를 얻는 것이 시간, 세계에 살고있는 사람들의 수 등과 같은 현재 세계 상태를 갖는 "세계"객체를 취한다는 것입니다. 객체는 항상 순수합니다. 즉, 같은 세계 상태를 통과하면 항상 같은 시간을 얻게됩니다.


1
"외부와 상호 작용하지 않는 모든 소프트웨어는 수학적 계산을하는 것 외에는 유용하지 않습니다." 내가 이해하는 한,이 경우에도 계산 입력은 프로그램에서 하드 코딩되어 유용하지 않습니다. 파일이나 터미널에서 입력 데이터를 수학 계산으로 읽으려면 불순한 코드가 필요합니다.
조르지오

1
@Ankur : 그것은 똑같은 것입니다. 프로그램이 그 자체가 아닌 다른 것 (예를 들어 키보드를 통한 세계)과 상호 작용하는 경우 여전히 불완전합니다.
정체성

1
@Ankur : 예, 당신 말이 맞아요! 명령 행에서 큰 입력 데이터를 전달하는 것이 실용적이지 않더라도 이것이 순수한 방법 일 수 있습니다.
Giorgio

2
세계에 살고있는 사람들의 수를 포함하여 "세계 물체"를 가지면 실행 컴퓨터가 거의 전지 수준으로 올라갑니다. 일반적인 경우는 HD에 몇 개의 파일이 있고 현재 사용자의 홈 디렉토리와 같은 것들이 포함되어 있다고 생각합니다.
ziggystar

4
@ziggystar- "world object"는 실제로 아무것도 포함하지 않습니다. 프로그램 외부에서 변화하는 세상의 상태를 나타내는 프록시 일뿐입니다. 그것의 유일한 목적은 타입 시스템이 식별 할 수있는 방식으로 변경 가능한 상태를 명시 적으로 표시하는 것입니다.
Kris Nuttycombe

7

귀하의 질문은 기능 / 제국 및 순수 / 불순이라는 두 가지 관련 컴퓨터 언어 측정 방법을 제공합니다.

기능적 언어는 기능의 입력과 출력 사이의 관계를 정의하고 명령형 언어는 수행 할 특정 순서로 특정 작업을 설명합니다.

순수한 언어는 부작용을 일으키거나 의존하지 않으며, 불순한 언어는 부작용을 전체적으로 사용합니다.

100 % 순수 프로그램은 기본적으로 쓸모가 없습니다. 흥미로운 계산을 수행 할 수 있지만 부작용이 없기 때문에 입력 또는 출력이 없으므로 계산 한 내용을 알 수 없습니다.

전혀 유용하기 위해서는 프로그램이 최소한 불순종해야합니다. 순수한 프로그램을 유용하게 만드는 한 가지 방법은 얇은 불순 래퍼 안에 넣는 것입니다. 테스트되지 않은 Haskell 프로그램과 같이 :

-- this is a pure function, written in functional style.
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

-- This is an impure wrapper around the pure function, written in imperative style
-- It depends on inputs and produces outputs.
main = do
    putStrLn "Please enter the input parameter"
    inputStr <- readLine
    putStrLn "Starting time:"
    getCurrentTime >>= print
    let inputInt = read inputStr    -- this line is pure
    let result = fib inputInt       -- this is also pure
    putStrLn "Result:"
    print result
    putStrLn "Ending time:"
    getCurrentTime >>= print

4
시간을내는 특정 문제를 해결할 수 있다면 우리가 IO가치와 결과를 순수하게 고려하는 정도에 대해 약간 설명했습니다 .
AndrewC

실제로, 100 % 순수한 프로그램조차도 CPU를 가열하는데 이는 부작용입니다.
Jörg W Mittag

3

함수형 프로그래밍, 즉 I / O 수행에서 매우 중요한 주제를 다루고 있습니다. 많은 순수 언어가 사용되는 방식은 포함 된 도메인 별 언어 (예 : 작업을 인코딩하는 작업을 수행 하는 하위 언어)를 사용 하는 것입니다. 결과를 얻을 수 있습니다.

예를 들어 Haskell 런타임은 main 프로그램을 구성하는 모든 동작으로 구성된 해야합니다. 그런 다음 런타임은이 조치를 실행합니다. 대부분의 경우 순수 코드를 실행합니다. 때때로 런타임은 계산 된 데이터를 사용하여 I / O를 수행하고 데이터를 순수한 코드로 다시 피드백합니다.

당신은 이것이 부정 행위처럼 들린다 고 불평 할 수도 있습니다. 액션을 정의하고 런타임이 실행을 기대함으로써, 프로그래머는 일반적인 프로그램이 할 수있는 모든 것을 할 수 있습니다. 그러나 Haskell의 강력한 유형 시스템은 프로그램의 순수 부분과 "불완전한"부분 사이에 강력한 장벽을 만듭니다. 즉, 현재 CPU 시간에 2 초를 더한 다음 인쇄 할 수는 없습니다. CPU 시간과 결과를 2 초간 추가하고 결과를 인쇄하는 다른 작업으로 전달합니다. 너무 많은 프로그램을 작성하는 것은 나쁜 스타일로 간주됩니다. 왜냐하면 값이 무엇인지 알 수있는 모든 것을 알려주는 Haskell 유형에 비해 어떤 효과가 발생했는지 추론하기 어렵 기 때문 입니다.

예 : clock_t c = time(NULL); printf("%d\n", c + 2);C에서는 main = getCPUTime >>= \c -> print (c + 2*1000*1000*1000*1000)Haskell에서. 연산자 >>=는 첫 번째 결과를 함수에 전달하여 두 번째 작업을 수행하는 작업을 작성하는 데 사용됩니다. 이처럼 매우 희귀 한 Haskell 컴파일러는 구문 설탕을 지원하여 다음과 같이 후자의 코드를 작성할 수 있습니다.

type Clock = Integer -- To make it more similar to the C code

-- An action that returns nothing, but might do something
main :: IO ()
main = do
    -- An action that returns an Integer, which we view as CPU Clock values
    c <- getCPUTime :: IO Clock
    -- An action that prints data, but returns nothing
    print (c + 2*1000*1000*1000*1000) :: IO ()

후자는 매우 필수적으로 보이지 않습니까?


1

그렇다면 어떻게 존재할 수 있습니까? 함수형 프로그래밍 원칙을 위반하지 않습니까? 특히 참조 투명성을 위반합니다.

순전히 기능적인 의미로는 존재하지 않습니다.

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

먼저 컴퓨터에서 시간을 검색하는 방법을 아는 것이 유용 할 수 있습니다. 기본적으로 시간을 추적하는 온보드 회로가 있습니다 (컴퓨터가 일반적으로 소형 셀 배터리가 필요한 이유). 그런 다음 특정 메모리 레지스터에서 시간 값을 설정하는 내부 프로세스가있을 수 있습니다. 본질적으로 CPU에서 검색 할 수있는 값으로 정리됩니다.


Haskell에는 IO 프로세스를 수행하기 위해 만들 수있는 유형을 나타내는 'IO action'개념이 있습니다. 따라서 time값을 참조하는 대신 값을 참조 IO Time합니다. 이 모든 것이 순전히 기능적 일 것입니다. 우리는 참조하는 time것이 아니라 '타임 레지스터의 값을 읽습니다' 라는 줄을 따라 무언가를 참조 합니다.

실제로 Haskell 프로그램을 실행하면 실제로 IO 동작이 발생합니다.

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