함수형 프로그래밍의 불변성이 실제로 존재합니까?


9

나는 일상 생활에서 프로그래머로 일하고 모든 유행 언어 (Python, Java, C 등)를 사용하지만 여전히 함수형 프로그래밍이 무엇인지에 대한 명확한 견해는 없습니다. 내가 읽은 것에서, 기능적 언어의 한 가지 속성은 데이터 구조가 변경 불가능 하다는 것 입니다. 나에게 이것은 혼자서 많은 질문을 제기합니다. 그러나 먼저 불변성에 대해 이해 한 내용을 약간 작성하고 틀리면 자유롭게 수정하십시오.

불변성에 대한 나의 이해 :

  • 프로그램이 시작되면 고정 데이터로 고정 데이터 구조를 갖습니다.
  • 이러한 구조에 새로운 데이터를 추가 할 수 없습니다
  • 코드에 변수가 없습니다
  • 이미 데이터 또는 현재 계산 된 데이터에서 "복사"할 수 있습니다.
  • 위의 모든 이유로 인해 불변성은 프로그램에 엄청난 공간 복잡성을 추가합니다.

내 질문 :

  1. 데이터 구조를 그대로 유지해야한다면 (불변), 누군가 어떻게 목록에 새 항목을 추가합니까?
  2. 새로운 데이터를 얻을 수없는 프로그램을 만드는 데있어 요점은 무엇입니까? 프로그램에 데이터를 공급하려는 센서가 컴퓨터에 연결되어 있다고 가정합니다. 수신 데이터를 어디에도 저장할 수 없다는 의미입니까?
  3. 이 경우 기능 프로그래밍이 기계 학습에 어떻게 도움이됩니까? 머신 러닝은 프로그램의 "인식"을 업데이트한다는 가정에서 구축되므로 새로운 데이터를 저장합니다.

2
함수 코드에 변수가 없다고 말하면 동의하지 않습니다. 수학적 의미에서“값 집합 중 하나를 가정 할 수있는 양”이라는 변수가 있습니다. 그것들은 변할 수 없지만 확실하지는 않습니다.
Édouard

1
혼동은 기능 언어에 대해 추상적으로 생각하기 때문이라고 생각합니다. Haskell의 어떤 프로그램이든 (예 : 콘솔에서 숫자 목록을 읽고, 빠르게 정렬하고, 출력하는) 프로그램을 사용하여 작동 방식과 의심을 어떻게 반증하는지 파악하십시오. 철학적 이라기보다는 실제 프로그램의 예를 보지 않고 실제로 정리할 방법이 없습니다. 하스켈 튜토리얼에서 많은 프로그램을 찾을 수 있습니다.
jkff

@jkff 무엇을 말하려고합니까? Haskel에는 기능이없는 기능이 있습니다. 문제는 Haskell이 아니라 함수형 프로그래밍에 관한 것입니다. 아니면 모든 기능적인 것을 주장하고 있습니까? 어떻게? 당신이 말했듯이, 철학에 어떤 문제가 있습니까? 추상화는 어떤 방식으로 혼란 스럽습니까? OP 질문은 매우 합리적인 질문입니다.
babou

@babou 나는 순수한 함수형 프로그래밍 언어가 알고리즘과 데이터 구조를 효율적으로 구현할 수있는 방법을 이해하는 가장 좋은 방법은 함수형 프로그래밍 언어로 효율적으로 구현 된 알고리즘과 데이터 구조의 예를 보는 것입니다. OP가 개념적으로 가능한 방법을 이해하려고 노력한 것 같습니다.이를 이해하는 가장 빠른 방법은 개념 설명을 읽는 것이 아니라 예제를 보는 것입니다.
jkff

함수형 프로그래밍을 보는 한 가지 방법은 부작용없이 프로그래밍하는 것입니다. 당신이 선택한 "유행"언어로 그렇게 할 수 있습니다. 모든 재 할당을 피하지 마십시오. 예를 들어 Java의 경우 모든 변수가 최종이고 모든 메소드가 읽기 전용입니다.
reinierpost

답변:


10

프로그램이 시작되면 고정 데이터로 고정 데이터 구조를 갖습니다.

이것은 약간의 오해입니다. 고정 형식과 고정 된 다시 쓰기 규칙 세트를 갖지만 이러한 다시 쓰기 규칙은 훨씬 더 큰 것으로 폭발 할 수 있습니다. 예를 들어, Haskell의 [1..100000000] 표현식은 매우 적은 양의 코드로 표현되지만 일반적인 형태는 방대합니다.

이러한 구조에 새로운 데이터를 추가 할 수 없습니다

예, 아니오 Haskell 또는 ML과 같은 언어의 기능적인 하위 집합은 외부 세계에서 데이터를 가져올 수 없지만 실제 프로그래밍을위한 모든 언어에는 외부 세계의 데이터를 순수한 기능 하위 집합에 삽입하는 메커니즘이 있습니다. Haskell에서 이것은 매우 신중하게 수행되지만 ML에서는 언제든지 원할 때 수행 할 수 있습니다.

코드에 변수가 없습니다

이것은 거의 사실이지만 이름을 지정할 수 없다는 생각과 혼동하지 마십시오. 항상 유용한 표현의 이름을 지정하고 끊임없이 재사용하십시오. 또한 ML과 Haskell, 내가 시도한 모든 Lisp 및 Scala와 같은 하이브리드에는 모두 변수를 만드는 수단이 있습니다. 그들은 일반적으로 사용되지 않습니다. 그리고 그러한 언어의 순전히 기능적인 부분 집합에는 그것들이 없습니다.

이미 데이터 또는 현재 계산 된 데이터에서 "복사"할 수 있습니다.

일반 형식으로 축소하여 계산을 수행 할 수 있습니다. 가장 좋은 방법은 실제로 계산을 수행하는 방법을보기 위해 기능 언어로 프로그램을 작성하는 것입니다.

예를 들어 "sum [1..1000]"은 수행하려는 계산이 아니지만 Haskell이 수행하는 작업입니다. 우리는 그것을 우리에게 의미가있는 작은 표현을 주었고 Haskell은 우리에게 해당 숫자를 주었다. 따라서 확실히 계산을 수행합니다.

데이터 구조를 그대로 유지해야한다면 (불변), 누군가 어떻게 목록에 새 항목을 추가합니까?

목록에 새 항목을 추가하지 않고 이전 항목으로 새 목록을 만듭니다. 이전 버전은 변경할 수 없기 때문에 새 목록이나 다른 곳에서 사용하는 것이 안전합니다. 이 스키마에서 훨씬 더 많은 데이터를 안전하게 공유 할 수 있습니다.

새로운 데이터를 얻을 수없는 프로그램을 만드는 데있어 요점은 무엇입니까? 프로그램에 데이터를 공급하려는 센서가 컴퓨터에 연결되어 있다고 가정합니다. 수신 데이터를 어디에도 저장할 수 없다는 의미입니까?

사용자 입력이 진행되는 한 실제 프로그래밍 언어는 사용자 입력을 얻는 방법이 있습니다. 이런 일이 발생합니다. 그러나 대부분의 코드를 작성하고 이러한 방식으로 장점을 활용하는 이러한 언어의 완전한 기능적 하위 집합이 있습니다.

이 경우 기능 프로그래밍이 기계 학습에 어떻게 도움이됩니까? 머신 러닝은 프로그램의 "인식"을 업데이트한다는 가정에서 구축되므로 새로운 데이터를 저장합니다.

이것은 활발한 학습의 경우이지만 내가 함께했던 대부분의 기계 학습 (기계 학습 그룹에서 코드 원숭이로 일하고 몇 년 동안 그렇게했습니다)에는 모든 교육 데이터가로드되는 일회성 학습 과정이 있습니다. 한 번에. 그러나 적극적인 학습을 위해서는 100 % 기능적으로 만 할 수 없습니다. 외부 세계에서 일부 데이터를 읽어야합니다.


공간 문제인 @Pithikos의 게시물에서 가장 중요한 점이 무엇인지 무시한 것 같습니다. 기능적 프로그램은 명령형 프로그램보다 공간을 더 많이 사용합니다 (현재 위치 알고리즘 등을 작성할 수 없음)
user541686

2
이것은 단순히 사실이 아닙니다. 돌연변이의 부족은 대부분 공유하고 당신이 언급 한 모든 크기 차이는 현대의 컴파일러에서 거의 작습니다. haskell의 목록에있는 대부분의 코드는 효율적으로 배치되었거나 메모리를 전혀 사용하지 않습니다.
Jake

1
나는 당신이 ML을 다소 잘못 표현한다고 생각합니다. 예, I / O는 어느 곳에서나 발생할 수 있지만 새로운 정보가 기존 구조에 도입되는 방식은 엄격하게 제어됩니다.
dfeuer

@Pithikos, 모든 곳에서 변수가 있습니다; 에두아르가 지적한 것처럼 그것들은 당신이 익숙한 것과는 다릅니다. 그리고 물건은 지속적으로 할당되고 가비지 수집됩니다. 실제로 함수형 프로그래밍을 시작하면 실제로 어떻게 진행되는지 더 잘 이해할 수 있습니다.
dfeuer

1
가장 잘 알려진 명령 구현과 같은 시간 복잡성을 가진 순수한 기능 구현이없는 알고리즘이 존재한다는 것은 사실입니다. 예를 들어 Union-Find 데이터 구조 (및 배열 :)) 공간에 대해서도 이와 같은 경우가 있다고 생각합니다. 복잡성. 그러나 이는 예외입니다. 가장 일반적인 알고리즘 / 데이터 구조에는 시간과 공간이 동등한 복잡한 구현이 있습니다. 프로그래밍 스타일과 컴파일러의 품질에 대한 주관적인 문제입니다.
jkff

4

불변성 또는 가변성은 함수형 프로그래밍에서 의미가있는 개념이 아닙니다.

계산 문맥

이것은 다른 최근 질문에 대한 흥미로운 후속 조치 (중복이 아님) 인 매우 좋은 질문입니다. 과제, 평가 및 이름 바인딩의 차이점은 무엇입니까?

나는 당신의 진술에 하나씩 대답하는 대신, 당신에게 위기에 처한 것에 대한 구조적 개요를 제공하려고 노력하고 있습니다.

다음과 같은 몇 가지 문제에 대한 답변이 있습니다.

  • 계산 모델이란 무엇이며 특정 모델에 적합한 개념

  • 사용하는 단어의 의미는 무엇이며 상황에 따라 어떻게 달라 집니까

함수형 프로그래밍 스타일은 명령형 프로그래머의 눈으로 볼 수 있기 때문에 어리석은 것처럼 보입니다. 그러나 그것은 다른 패러다임이며, 당신의 명령 개념과 인식은 외계인입니다. 컴파일러에는 그러한 편견이 없습니다.

그러나 최종 결론은 기계 학습을 포함하여 순수하게 기능적인 방식으로 프로그램을 작성할 수 있다는 것입니다. 기능적 프로그래밍에는 데이터 저장 개념이 없다고 생각했습니다. 나는이 시점에서 다른 답변에 동의하지 않는 것 같습니다.

희망적 으로이 답변의 길이에도 불구하고 소수의 사람들이 관심을 가질 것입니다.

계산 패러다임

문제는 이론적이며 가장 간단한 대표가 람다 미적분학 인 특정 계산 모델 인 기능적 프로그래밍 (일명 응용 프로그래밍)에 관한 것입니다.

이론적 인 수준에 머무르면 튜링 머신 (TM), RAM 머신 및 기타 머신 , 람다 미적분, 조합 논리, 재귀 함수 이론, 반 색조 시스템 등 많은 계산 모델이 있습니다. 모델은 그들이 다룰 수있는 것의 측면에서 동등한 것으로 입증되었으며, 이것이 교회 튜링 논문 의 요지입니다 .

중요한 개념은 모델을 서로 축소하는 것인데, 이것은 교회-튜링 논문으로 이어지는 동등성을 확립하기위한 기초입니다. 프로그래머의 관점에서 볼 때 한 모델을 다른 모델로 줄이는 것은 보통 컴파일러라고하는 것입니다. 논리 프로그래밍을 계산 모델로 사용하면 상점에서 구입 한 PC에서 제공하는 모델과는 상당히 다르며 컴파일러는 논리 프로그래밍 언어로 작성된 프로그램을 PC가 나타내는 계산 모델로 변환합니다. RAM 컴퓨터).

그러나 두 모델이 동일한 방식으로 작동하거나 특정 개념이 다른 개념으로 이전 될 수 있다는 의미는 아닙니다. 일반적으로 TM의 계산 단계는 (β-) Lambda 미적분의 감소 단계는 서로 번역 가능합니다. 람다 식의 최적 평가 개념은 TM 모델의 복잡성 문제와는 거리가 멀다.

실제로, 우리가 사용하는 프로그래밍 언어는 서로 다른 이론적 기원의 개념을 혼합하는 경향이 있으며,이를 위해 프로그램의 선택된 부분이 적절한 경우 일부 모델의 속성을 활용할 수 있습니다. 마찬가지로, 시스템을 구축하는 사람들은 현재 작업에 가장 적합한 언어로 구성 요소마다 다른 언어를 선택할 수 있습니다.

따라서 프로그래밍 언어에서 순수한 상태의 프로그래밍 패러다임을 보는 경우는 거의 없습니다. 프로그래밍 언어는 여전히 지배적 인 패러다임에 따라 분류되지만, 다른 패러다임의 개념이 관련 될 때 언어의 속성에 영향을 줄 수 있으며, 종종 구별과 개념적 문제를 흐리게합니다.

일반적으로 Haskell 및 ML 또는 CAML과 같은 언어는 기능적인 것으로 간주되지만 명령형 동작을 허용 할 수 있습니다. 그렇지 않으면 왜 " 순전히 기능적인 하위 집합 "에 대해 이야기 합니까?

그런 다음 기능 프로그래밍 언어 로이 작업을 수행 할 수 있다고 주장 할 수는 있지만 추가 기능으로 간주 될 수있는 것에 의존 할 때 기능 프로그래밍에 대한 질문에 실제로 대답하지는 않습니다.

답변은 추가 사항없이 특정 패러다임과 더 정확하게 관련되어야합니다.

변수 란 무엇입니까?

또 다른 문제는 용어 사용입니다. 수학에서 변수는 일부 도메인에서 결정되지 않은 값을 나타내는 엔티티입니다. 다양한 목적으로 사용됩니다. 방정식에서 사용되면 방정식이 확인되도록 모든 값을 나타낼 수 있습니다. 이 비전은 아마도 논리 프로그래밍이 개발 될 때 이름 변수가 이미 다른 의미를 가지고 있었기 때문에 " 논리 변수 "라는 이름으로 논리 프로그래밍에 사용됩니다.

전통적인 명령형 프로그래밍에서 변수는 값의 표현을 기억하고 현재 값을 다른 것으로 대체 할 수있는 일종의 컨테이너 (또는 메모리 위치)로 이해됩니다.

함수형 프로그래밍에서 변수는 어떤 가치를위한 자리 표시 자와 같이 수학에서 사용하는 것과 같은 목적을 갖지만 아직 제공되지는 않습니다. 전통적인 명령형 프로그래밍에서이 역할은 실제로 상수에 의해 수행됩니다 ( 123, true, [ "abdcz", 3.14]와 같은 값의 도메인에 고유 한 표기법으로 표현 된 값으로 결정된 리터럴 과 혼동되지 않아야 함 ).

상수뿐만 아니라 모든 종류의 변수는 식별자로 나타낼 수 있습니다.

명령형 변수는 값을 변경할 수 있으며 이는 가변성의 기초입니다. 기능 변수는 불가능합니다.

프로그래밍 언어는 일반적으로 언어의 작은 개체에서 더 큰 개체를 만들 수 있도록합니다.

명령형 언어를 사용하면 이러한 구문에 변수를 포함시킬 수 있으며 이는 가변 데이터를 제공하는 것입니다.

프로그램을 읽는 방법

프로그램은 기본적으로 알고리즘에 대한 추상적 인 설명입니다. 실용적인 디자인이든 패러다임 적으로 순수한 언어이든 언어입니다.

원칙적으로 추상적으로 의미하는 바에 대한 모든 진술을 취할 수 있습니다. 그런 다음 컴파일러는이를 컴퓨터가 실행하기에 적합한 형식으로 변환하지만 첫 번째 근사치의 문제는 아닙니다.

물론, 현실은 조금 더 거칠고, 컴파일러가 효율적인 실행을 처리하는 방법을 알지 못하는 구조를 피하기 위해 어떤 일이 발생하는지에 대해 잘 알고있는 것이 좋습니다. 그러나 그것은 이미 최적화입니다 ... 어떤 컴파일러는 프로그래머보다 훨씬 좋을 수 있습니다.

기능적 프로그래밍 및 가변성

변경 가능성은 할당에 의해 변경 될 값을 포함 할 수있는 명령 변수의 존재를 기반으로합니다. 이것들은 함수형 프로그래밍에는 존재하지 않기 때문에 모든 것이 불변으로 보일 수 있습니다.

함수형 프로그래밍은 값만 처리합니다.

불변성에 대한 첫 네 가지 진술은 대부분 정확하지만 명령이 아닌 것을 명령 식으로 묘사하십시오. 그것은 모두가 장님 인 세상에서 색상으로 묘사하는 것과 약간 같습니다. 함수형 프로그래밍에 익숙하지 않은 개념을 사용하고 있습니다.

순수한 값만 있고 정수 배열은 순수한 값입니다. 한 요소에서만 다른 다른 배열을 얻으려면 다른 배열 값을 사용해야합니다. 요소를 변경하는 것은이 맥락에서 존재하지 않는 개념 일뿐입니다. 배열과 일부 색인을 인수로 갖는 함수를 가질 수 있으며, 색인으로 표시된 위치 만 다른 거의 동일한 배열 인 결과를 리턴합니다. 그러나 여전히 독립 배열 값입니다. 이러한 가치가 어떻게 표현 되는가는 문제가 아닙니다. 어쩌면 그들은 컴퓨터의 명령 번역에서 많은 것을 "공유"하지만 컴파일러의 일입니다 ... 그리고 어떤 종류의 기계 아키텍처가 컴파일되고 있는지 알고 싶지 않습니다.

당신은 값을 복사 하지 않습니다 (이치가 없으며 외계인의 개념입니다). 프로그램에서 정의한 도메인에 존재하는 값만 사용하십시오. 리터럴로 설명하거나 다른 값에 함수를 적용한 결과입니다. 프로그램의 다른 위치에서 동일한 값이 사용되도록 이름을 지정하여 상수를 정의 할 수 있습니다. 함수 어플리케이션은 계산이 아니라 주어진 인수에 대한 어플리케이션의 결과로 인식되어야합니다. 쓰기 5+2또는 쓰기 7같은 금액을. 이전 단락과 일치합니다.

명령형 변수가 없습니다. 할당이 불가능합니다. 지정 가능한 변수에 이름을 바인딩 할 수있는 명령형 언어와 달리 이름 만 값에 바인딩하여 상수를 형성 할 수 있습니다.

복잡한 비용이 있는지는 확실하지 않습니다. 우선, 복잡성 우려 명령형 패러다임에 대해 언급합니다. 기능적 프로그램을 명령형 프로그램으로 읽도록 선택하지 않는 한, 기능적 프로그래밍에 대해서는 그렇게 정의되지 않습니다. 이는 디자이너의 의도가 아닙니다. 실제로 기능적 관점은 이러한 문제에 대해 걱정하지 않고 계산 대상에 집중할 수 있도록하기위한 것입니다. 그것은 사양과 구현과 약간 비슷합니다.

컴파일러는 구현에주의를 기울여야하며, 수행 할 하드웨어에 무엇을해야하는지에 대해 가장 잘 적응할 수있을 정도로 똑똑해야합니다.

나는 프로그래머가 그것에 대해 걱정하지 않는다고 말하는 것이 아닙니다. 또한 프로그래밍 언어와 컴파일러 기술이 원하는만큼 성숙하다고 말하는 것은 아닙니다.

질문에 대답

  1. 기존 값 (외계인 개념)은 수정하지 않지만 원하는 경우 다른 요소를 하나 추가하여 목록에있는 새 값을 계산하십시오.

  2. 프로그램은 새로운 데이터를 얻을 수 있습니다. 요점은 언어로 표현하는 방법입니다. 예를 들어, 프로그램이 크기 제한이없는 하나의 특정 값 (입력 스트림이라고 함)으로 작동한다고 생각할 수 있습니다. 그것은 거기에 앉아 있어야 할 가치입니다 (이미 완전히 알려 졌는지 아닌지 당신의 문제가 아닌지). 그런 다음 스트림의 첫 번째 요소와 나머지 스트림으로 구성된 쌍을 반환하는 함수가 있습니다.

    이를 사용하여 순전히 적용 방식으로 통신 구성 요소의 네트워크를 구축 할 수 있습니다 (코 루틴)

  3. 머신 러닝은 데이터를 정확하게 작성하고 값을 수정해야 할 때 또 다른 문제입니다. 함수형 프로그래밍에서는 그렇게하지 않습니다. 훈련 데이터에 따라 적절히 다른 새 값만 계산하면됩니다. 결과 기계도 작동합니다. 걱정하는 것은 컴퓨팅 시간과 공간 효율성입니다. 그러나 다시 말하지만 이는 컴파일러가 이상적으로 처리해야하는 다른 문제입니다.

최종 비고

의견이나 다른 답변에서 실용적인 함수형 프로그래밍 언어는 순전히 기능하지 않습니다. 이는 특히 컴파일과 관련하여 기술이 여전히 개선되어야한다는 사실을 반영한 것입니다.

순전히 적용 가능한 스타일로 글을 쓸 수 있습니까? 그 대답은 약 40 년 동안 알려져 왔으며 "예"입니다. 1970 년대에 등장한 denatotional semantics의 목적은 언어를 순전히 기능적인 스타일로 정확하게 번역 (컴파일)하고 수학적으로 더 잘 이해하는 것으로 간주되어 프로그램의 의미를 정의하기위한 더 나은 자금으로 간주되었습니다.

흥미로운 점은 변수를 포함한 명령형 프로그래밍 구조가 데이터 저장소와 같은 적절한 값의 도메인을 도입함으로써 기능적 스타일로 변환 될 수 있다는 것입니다. 그리고 기능적 스타일에도 불구하고, 명령형 스타일로 작성된 실제 컴파일러의 코드와 놀랍게도 유사합니다.


0

기능적 프로그램이 데이터를 저장할 수 없다는 오해이며, Jakes가 실제로 이것을 잘 설명하지 못했다고 생각합니다.

기능적 프로그램은 다른 프로그램과 마찬가지로 실제로 정수를 정수로 매핑하는 기능을합니다. 변경 가능한 데이터 구조에서 작동하는 모든 명령형 프로그램에는 기능적인 기능이 있습니다. 이것은 같은 목표를 달성하는 또 다른 수단 일뿐입니다.

일부 소스로부터 실험 데이터를 저장하는 기능적 방법은 인수로서 데이터 구조를 갖는 저장 기능을 호출하고 기존 데이터 구조와 새로운 데이터의 연결을 출력하므로 데이터는 변경 가능한 데이터 구조의 개념없이 저장된다.

내 자신의 경험에서 , 불변의 데이터 구조의 개념은 기존의 개발자가 기능적 환경에서 수행하기가 불가능하거나 불가능한 특정 일이 있다고 생각하는 기존 개발자를 이끌고 있다고 생각합니다. 그렇지 않다.


"기능 프로그램은 다른 프로그램과 마찬가지로 실제로 정수를 정수로 매핑하는 기능을합니다." 예를 들어, Minecraft는 실제로 정수를 정수에 매핑하는 함수는 무엇입니까?
David Richerby

용이하게. 모든 바이트는 이진 정수로 해석 될 수 있습니다. 컴퓨터의 상태는 바이트 모음입니다. Minecraft조차도 프로그램은 컴퓨터 상태를 조작하여 한 상태에서 다른 상태로 매핑합니다.
Jeppe Hartmund

사용자 입력이이 세상에 맞지 않는 것 같습니다.
David Richerby

사용자 입력은 컴퓨터 상태의 일부입니다. 화면에만 존재하는 것은 아닙니다.
Jeppe Hartmund
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.