어떤 기능적 기능이 가져다주는 이점에 대해 약간의 OOP 혼동 가치가 있습니까?


13

Haskell과 F #에서 함수형 프로그래밍을 배우고 나면 OOP 패러다임은 클래스, 인터페이스, 객체와 거꾸로 보입니다. 동료가 이해할 수있는 FP의 어떤 측면을 업무에 도입 할 수 있습니까? 팀을 재교육하여 사용할 수 있도록 상사와 이야기 할 가치가있는 FP 스타일이 있습니까?

FP의 가능한 측면 :

  • 불변성
  • 부분 적용 및 카레
  • 일급 함수 (함수 포인터 / 기능적 개체 / 전략 패턴)
  • 게으른 평가 및 Monads
  • 순수한 기능 (부작용 없음)
  • 표현식 (vs. 명령문-각 코드 행은 부작용 대신 또는 부작용을 유발하는 것 외에 값을 생성 함)
  • 재귀
  • 패턴 매칭

프로그래밍 언어가 지원하는 한도에서 언어가 지원하는 한도까지 자유롭게 할 수 있습니까? 아니면 더 나은 지침이 있습니까?


6
나는 비슷한 경험을했다. 약 2 개월의 고통 끝에 나는 "물체에 매핑되는 것들"과 "함수에 매핑되는 것들"의 균형이 잘 잡히기 시작했습니다. 두 언어를 모두 지원하는 언어로 심각한 해킹을하는 데 도움이됩니다. 그것의 끝에서, 모두 내 FP와 OOP 기술이 크게 향상
다니엘 Gratzer에게

3
FWIW에서 Linq는 기능 적이고 게으 르며 정적 메서드를 사용하고 상태 지속성을 피함으로써 C #에서 기능 프로그래밍을 시뮬레이션 할 수 있습니다.
Robert Harvey

1
이 시점에서 sicp 를 읽어야 합니다 . 무료이며 잘 작성되었습니다. 두 패러다임 사이의 훌륭한 비교를 제공합니다.
Simon Bergot

4
FP와 OOP는 동시에 어떤 의미에서는 직교적이고 어떤 의미에서는 이중적입니다. OOP는 데이터 추상화에 관한 것이고, FP는 부작용에 관한 것입니다. 부작용이 있는지 여부는 데이터를 추상화하는 방법과 직교합니다. Lambda Calculus는 예를 들어 기능적이며 객체 지향적입니다. 예, FP는 일반적으로 객체가 아닌 추상 데이터 유형을 사용하지만 FP가 적지 않고 객체를 사용할 수도 있습니다. OTOH 와도 깊은 관계가 있습니다. 함수는 단 하나의 방법으로 객체에 동형 적입니다. 즉, Java에서 "가짜"방식으로 Java8에서 구현되는 방식입니다.
Jörg W Mittag

3
귀하의 질문의 가장 큰 측면은 가독성과 관련이 있다고 생각합니다. "객체 지향 상점에서 작업하기 위해 얼마나 많은 기능적 프로그래밍 스타일이 적합합니까?" 또는 기능상의 이점으로 인해 OOP가 혼동 될만한 가치가 있습니다.
GlenPeterson

답변:


13

함수형 프로그래밍은 객체 지향 프로그래밍과는 다른 패러다임입니다 (다른 사고 방식 및 프로그램에 대한 다른 사고 방식). 여기에 문제와 그 해결책에 대해 생각할 수있는 하나 이상의 방법 (객체 지향)이 있다는 것을 깨닫기 시작했습니다. 다른 것들이 있습니다 (절차 및 일반 프로그래밍이 떠 오릅니다). 이러한 새로운 도구와 접근 방식을 수용하고 기술 세트에 통합했는지 여부에 관계없이이 새로운 지식에 대응하는 방법은 성장하고보다 완전하고 숙련 된 개발자가 될지 여부를 결정합니다.

우리는 모두 다루기 위해 훈련을 받았으며 특정 수준의 복잡성에 익숙합니다. 나는 이것을 사람의 시간 제한 이라고 부르고 싶습니다 (Watership Down에서 얼마나 높을 수 있습니까). 마음을 넓히고 더 많은 옵션을 고려할 수있는 능력을 키우고 더 많은 도구를 사용하여 문제를 해결하고 해결하는 것이 좋습니다. 그러나 그것은 변화이며, 그것은 당신을 당신의 안락 지대에서 끌어냅니다.

당신이 겪을 수있는 한 가지 문제는 "모든 것이 하나의 대상"군중을 따르는 데 덜 만족한다는 것입니다. 소프트웨어 개발에 대한 기능적 접근 방식이 특정 문제에 대해 잘 작동하는 이유를 이해하지 못하거나 이해하려는 사람들과 함께 일할 때 인내심을 개발해야 할 수도 있습니다. 일반적인 프로그래밍 방식이 특정 문제에 대해 잘 작동하는 것처럼.

행운을 빕니다!


3
또한 Haskell 또는 Clojure와 같은 기능적 언어로 작업 할 때 일부 전통적인 OOP 개념을 더 잘 이해할 수 있다고 덧붙이고 싶습니다. 개인적으로 나는 다형성이 실제로 중요한 개념 (자바의 인터페이스 또는 Haskell의 유형 클래스) 인 방법을 깨달았지만 상속 (정의 된 개념이라고 생각한 것)은 이상한 추상화입니다.
wirrbel

6

함수형 프로그래밍은 일상적인 코드 작성에서 매우 실용적이고 철저한 생산성을 제공합니다. 일부 기능은 간결함을 선호합니다. 코드 작성 횟수가 적고 오류가 적으며 유지 관리 횟수가 적기 때문에 유용합니다.

수학자이기 때문에 멋진 기능적 요소가 매우 매력적이지만 응용 프로그램을 설계 할 때 유용합니다. 이러한 구조는 변수로 이러한 불변성을 나타내지 않고 프로그램 구조에서 프로그램의 많은 불변을 인코딩 할 수 있습니다.

내가 좋아하는 조합은 매우 사소한 것처럼 보일 수 있지만 생산성에 매우 큰 영향을 미친다고 생각합니다. 이 조합은 부분 응용 프로그램 및 CurryingFirst Class 함수 이며 레이블을 다시 지정 하면 for 루프를 다시 작성하지 않습니다 . 대신 루프 본문을 반복 또는 매핑 함수에 전달하십시오. 나는 최근에 C ++ 작업에 고용되었고 재미있게도 루프 를 쓰는 습관을 잃어 버렸습니다 !

재귀패턴 일치 의 조합은 방문자 디자인 패턴 의 필요성을 소멸시킵니다 . 부울 식의 평가자를 프로그래밍하는 데 필요한 코드를 비교하면됩니다. 함수형 프로그래밍 언어에서이 코드는 약 15 줄이어야합니다. OOP에서 해야 할 일은 방문자 디자인 패턴 을 사용하는 것 입니다. 광범위한 에세이. 장점은 분명하며 불편한 점이 없습니다.


2
나는 완전히 동의하지만 업계 전반에 걸쳐 동의하는 경향이있는 사람들로부터 반발했습니다. 그들은 방문자 패턴을 알고 있으며 여러 번 보았고 사용했기 때문에 코드를 이해하고 친숙한 것입니다. 엄청나게 간단하고 쉬우면서도 다른 접근 방식은 이질적이므로 더 어려워집니다. 이것은 불행히도 15 년 이상의 OOP가 모든 프로그래머들에게 영향을 미쳤다는 사실입니다. 10 년 이상 100 번 이상 반복 한 후 100 번 이상 줄을 암기했기 때문에 100 번 이상의 코드 줄이 10 개보다 이해하기 쉽습니다.
Jimmy Hoffa

1
더 간결한 코드가 "더 적은"코드를 작성한다는 의미는 아닙니다. 적은 문자를 사용하여 동일한 코드를 작성하고 있습니다. 어떤 경우 에는 코드를 읽기가 더 어렵 기 때문에 더 많은 오류가 발생합니다.
Telastyn

8
@Telastyn : Terse는 읽을 수없는 것과 다릅니다. 또한, 대량의 bloated 상용구는 읽을 수없는 고유 한 방식을 가지고 있습니다.
Michael Shaw

1
@Telastyn 나는 당신이 여기에서 실제 요점을 만졌다 고 생각합니다. 그렇습니다. 너무 나쁘고 읽을 수 없으며, 부풀어 오른 것도 나쁘고 읽을 수 없지만 열쇠는 가변 길이가 아니며 수수께끼로 작성된 코드가 아닙니다. 열쇠는 당신이 작업의 수 위의 언급대로, 내가 (명확하게 작성된 코드) 적은 일을 생각, 작업의 수는 유지 보수에없는 상관 관계 않습니다 반대입니다 않는 혜택 가독성과 유지 보수를. 단일 문자 기능과 변수 이름으로 같은 수의 작업을 수행해도 도움이되지 않습니다. 좋은 FP는 여전히 명확하게 작성된 작업 이 훨씬 적습니다.
Jimmy Hoffa

2
@ user949300 : 완전히 다른 예제를 원한다면이 Java 8 예제는 어떻습니까? : list.forEach(System.out::println);FP 관점에서 println두 가지 인수, 대상 PrintStream과 값을 취하는 함수 Object이지만 CollectionforEach메소드는 하나의 인수 만있는 함수를 기대합니다 모든 요소에 적용 할 수 있습니다. 따라서 첫 번째 인수는 System.out하나의 인수로 새 함수를 생성 할 때 찾은 인스턴스에 바인딩됩니다 . 보다 간단합니다BiConsumer<…> c=PrintStream::println; PrintStream a1=System.out; list.forEach(a2 -> c.accept(a1, a2));
Holger

5

직장에서 사용하는 지식의 일부, 슈퍼맨이 평범한 삶의 특권을 누리기 위해 클라크 켄트 인 척하는 방식을 제한해야 할 수도 있습니다. 그러나 더 많은 것을 아는 것은 결코 당신을 해치지 않을 것입니다. 즉, 기능적 프로그래밍의 일부 측면은 객체 지향 상점에 적합하며 다른 측면은 상점의 평균 지식 수준을 높이고 결과적으로 더 나은 코드를 작성할 수 있도록 상사와 이야기 할 가치가 있습니다.

FP와 OOP는 상호 배타적이지 않습니다. 스칼라를보십시오. 어떤 사람들은 그것이 불순한 FP이기 때문에 그것이 최악이라고 생각하지만, 어떤 사람들은 그것이 같은 이유로 최고라고 생각합니다.

하나씩 OOP와 함께 잘 작동하는 몇 가지 측면이 있습니다.

  • 순수한 기능 (부작용 없음)-내가 아는 모든 프로그래밍 언어가이를 지원합니다. 그것들은 코드를 훨씬 쉽고 추론하기 쉬우 며 실용 할 때마다 사용해야합니다. FP라고 할 필요는 없습니다. 좋은 코딩 관행이라고 부르십시오.

  • 불변성 : 문자열은 틀림없이 가장 일반적으로 사용되는 Java 객체이며 불변입니다. 내 블로그에서 변경 불가능한 Java 오브젝트변경 불가능한 Java 콜렉션 을 다룹니다 . 그 중 일부는 당신에게 적용 할 수 있습니다.

  • 퍼스트 클래스 함수 (함수 포인터 / 함수 객체 / 전략 패턴)-Java는 리스너 인터페이스를 구현하는 대부분의 API 클래스 (및 수백 개)가있는 버전 1.1 이후로 돌연변이가있는 돌연변이 버전을 가지고 있습니다. Runnable은 아마도 가장 일반적으로 사용되는 기능적 객체 일 것입니다. 퍼스트 클래스 함수는 기본적으로 지원하지 않는 언어로 코드를 작성하는 데 더 많은 노력을 기울이지 만 때로는 코드의 다른 측면을 단순화 할 때 추가 노력을 기울일 가치가 있습니다.

  • 재귀는 트리를 처리하는 데 유용합니다. OOP 상점에서는 아마도 재귀를 적절하게 사용하는 것입니다. OOP에서 재미를 위해 재귀를 사용하는 것은 대부분의 OOP 언어가 기본적으로 스택 공간을 갖지 않는 것이 좋은 아이디어가 될 수 있다면 어리둥절해야합니다.

  • 표현식 (vs. 명령문-각 코드 행은 부작용 대신 또는 부작용을 유발하는 값을 생성합니다)-C, C ++ 및 Java의 유일한 평가 연산자는 3 차 연산자 입니다. 내 블로그에서 적절한 사용법에 대해 이야기합니다. 재사용 성이 높고 평가적인 간단한 함수를 작성할 수 있습니다.

  • 지연 평가 (및 모나드)-대부분 OOP의 지연 초기화로 제한됩니다. 언어 기능을 지원하지 않으면 유용한 API를 찾을 수 있지만 직접 작성하기는 어렵습니다. 대신 스트림 사용을 극대화하십시오. 예제는 라이터 및 리더 인터페이스를 참조하십시오.

  • 부분 적용 및 카레-일급 기능이 없으면 실용적이지 않습니다.

  • 패턴 일치-일반적으로 OOP에서는 권장하지 않습니다.

요약하면, 프로그래밍 언어가 지원하는 한도 내에서 언어가 지원하는 한도 내에서 작업을 자유롭게 수행 할 수 있다고 생각하지 않습니다. 동료의 가독성은 고용을 위해 만든 코드에 대한 리트머스 테스트라고 생각합니다. 그것이 당신을 가장 괴롭히는 곳에서 나는 직장 동료들의 시야를 넓히기 위해 직장에서 교육을 시작하려고합니다.


FP를 배우기 때문에 나는 표현과 비슷한 결과물을 만들어내는 유창한 인터페이스를 갖도록 설계하는 습관을 가졌습니다. C #에서 정적 확장 메소드를 사용하면 더 이상 void 메소드가 없다는 것을 알게되면 순도에서 자연스럽게 흘러가는 접근법입니다. 그런 식으로 당신의 표현 포인트는 내가 동의하지 않는 유일한 포인트입니다. 다른 모든 것들은 FP를 배우고 .NET 주간 일을하는 내 자신의 경험으로
드러납니다

C #에서 실제로 나를 귀찮게하는 것은 두 가지 간단한 이유로 인해 하나의 메서드 인터페이스 대신 대리자를 사용할 수 없다는 것입니다. 결합기 (C #에서 지옥만큼 추한 것입니다). 2. 프로젝트 범위에서 사용할 수있는 유형 별칭이 없으므로 대리자의 서명을 매우 빠르게 관리 할 수 ​​없게됩니다. 따라서이 두 가지 어리석은 이유 때문에 더 이상 C #을 즐길 수 없습니다. 왜냐하면 내가 할 수있는 유일한 것은 불필요한 추가 작업 인 단일 메서드 인터페이스를 사용하는 것입니다.
Trident D' Gao

@bonomo Java 8에는 C #에서 유용 할 수있는 일반적인 java.util.function.BiConsumer가 있습니다 public interface BiConsumer<T, U> { public void accept(T t, U u); }. java.util.function에는 다른 유용한 기능 인터페이스가 있습니다.
GlenPeterson

@bonomo 안녕하세요, 하스켈의 고통입니다. "FP를 배우면 OOP에서 나아졌다"는 말을 읽을 때마다 루비 나 하스켈처럼 순수하고 선언적이지 않은 것을 배웠습니다. Haskell은 OOP가 쓸데없이 낮은 수준임을 분명히합니다. 가장 큰 고통은 HM 유형 시스템에 있지 않을 때 제약 조건 기반 유형 유추를 결정할 수 없으므로 cosntraint 기반 유형 유추가 완전히 수행되지 않는다는 것입니다. blogs.msdn.com/b/ericlippert/archive / 2012 / 03 / 09 /…
Jimmy Hoffa

1
Most OOP languages don't have the stack space for it 정말? 균형 잡힌 이진 트리에서 수십억 개의 노드를 관리하기 위해 30 레벨의 재귀가 필요합니다. 내 스택 공간이 이것보다 더 많은 레벨에 적합하다고 확신합니다.
Robert Harvey

3

함수형 프로그래밍 및 객체 지향 프로그래밍 외에도 선언적 프로그래밍 (SQL, XQuery)도 있습니다. 각 스타일을 배우면 새로운 통찰력을 얻는 데 도움이되며 직무에 적합한 도구를 선택하는 방법을 배우게됩니다.

그러나 언어로 코드를 작성하는 것은 매우 실망 스러울 수 있으며 다른 것을 사용하는 경우 특정 문제 영역에서 생산성이 높아질 수 있습니다. 그러나 Java와 같은 언어를 사용하더라도 라운드 방식으로 FP에서 Java 코드에 개념을 적용 할 수 있습니다. 예를 들어 Guava 프레임 워크는이 중 일부를 수행합니다.


2

프로그래머로서 나는 당신이 학습을 멈추지 않아야한다고 생각합니다. FP를 배우는 것이 OOP 기술을 오염시키고 있다는 것은 매우 흥미 롭습니다. 저는 자전거를 타는 방법을 배우는 것으로 OOP를 배우는 경향이 있습니다. 당신은 그것을하는 방법을 잊지 마십시오.

FP의 장단점을 알게되면서 수학적으로 더 수학적으로 생각하고 소프트웨어를 작성하는 방법에 대한 더 나은 관점을 얻었습니다. 그것은 나의 개인적인 경험입니다.

경험이 많을수록 핵심 프로그래밍 개념을 잃기가 훨씬 어려워집니다. 따라서 OOP 개념이 완전히 구체화 될 때까지 FP를 쉽게 사용할 것을 제안합니다. FP는 명확한 패러다임 전환입니다. 행운을 빕니다!


4
학습 OOP는 크롤링 학습과 같습니다. 그러나 발을 꾸준히 밟으면 취한 상태에서만 크롤링에 의존하게됩니다. 물론 그렇게하는 방법을 잊을 수는 없지만 일반적으로 원하지는 않습니다. 그리고 당신이 달릴 수있을 때 크롤러와 함께 걷는 것은 고통스러운 경험이 될 것입니다.
SK-logic

@ SK-logic, 나는 당신의 메타포를 좋아합니다
Trident D' Gao

@ SK-Logic : 학습 명령형 프로그래밍이란 무엇입니까? 당신의 뱃속에 자신을 드래그?
Robert Harvey

@RobertHarvey는 녹슨 숟가락과 펀치 카드 데크로 지하를 파 내려했습니다.
Jimmy Hoffa

0

이미 많은 좋은 답변이 있으므로 내 질문의 일부를 해결할 것입니다. 즉, OOP와 기능적 기능이 상호 배타적이지 않기 때문에 귀하의 질문에 전념합니다.

C ++ 11을 사용하는 경우 언어 / 표준 라이브러리에 내장 된 OOP와 시너지 효과를 제공하는 기능 프로그래밍 기능이 많이 있습니다. 물론 상사 나 동료가 TMP를 얼마나 잘받을 수 있을지 잘 모르겠지만 요점은 C ++과 같은 비 기능적 / OOP 언어로 이러한 기능을 여러 형태로 얻을 수 있다는 것입니다.

컴파일 시간 재귀와 함께 템플릿을 사용하는 것은 처음 3 점에 의존합니다.

  • 불변성
  • 재귀
  • 패턴 매칭

이 템플릿 값은 변경할 수 없으며 (컴파일 타임 상수) 모든 반복은 재귀를 사용하여 수행되고 분기는 오버로드 확인 형식으로 패턴 일치를 사용하여 수행됩니다.

다른 점에 관해서는 부분 함수 적용을 사용 std::bind하고 std::function제공하며 함수 포인터는 언어에 내장되어 있습니다. 호출 가능한 객체는 기능적 객체 (부분 기능 응용 프로그램)입니다. 호출 가능한 객체는 객체를 정의하는 객체를 의미합니다 operator ().

게으른 평가와 순수한 기능은 조금 더 어려울 것입니다. 순수한 함수의 경우 값으로 만 캡처하는 람다 함수를 사용할 수 있지만 이는 바람직하지 않습니다.

마지막으로, 부분 함수 애플리케이션에서 컴파일 타임 재귀를 사용하는 예제가 있습니다. 다소 고안된 예이지만 위의 대부분의 요점을 보여줍니다. 주어진 튜플의 값을 주어진 함수에 재귀 적으로 바인딩하고 (호출 가능한) 함수 객체를 생성합니다.

#include <iostream>
#include <functional>

//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};

//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
  : gen_indices<N-1, N-1, Seq ... >
{};

template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
    typedef index_seq<Seq ... > type;
};


template <typename RType>
struct bind_to_fcn
{
    template <class Fcn, class ... Args>
    std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
    {
        return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
    }

    template<std::size_t ... Seq, class Fcn, class ... Args>
    std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
    {
        return std::bind(fcn, std::get<Seq>(params) ...);
    }
};

//some arbitrary testing function to use
double foo(int x, float y, double z)
{
    return x + y + z;
}

int main(void)
{
    //some tuple of parameters to use in the function call
    std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);                                                                                                                                                                                                      
    typedef double(*SumFcn)(int,float,double);

    bind_to_fcn<double> binder;
    auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
    std::cout << other_fcn_obj() << std::endl;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.