함수형 프로그래밍을 배우는 작업을 스스로 결정했습니다. 지금까지 폭발이었고, 나는 ‘빛을 보았습니다’. 불행히도, 나는 실제로 질문을 반송 할 수있는 기능 프로그래머를 모른다. 스택 교환을 소개합니다.
웹 / 소프트웨어 개발 과정을 수강하지만 강사는 기능적 프로그래밍에 익숙하지 않습니다. 그는 그것을 사용하는 것이 좋으며, 방금 코드를 더 잘 읽을 수 있도록 작동 방식을 이해하도록 도와달라고 요청했습니다.
가장 좋은 방법은 가치를 거듭 제곱하는 것과 같은 간단한 수학 함수를 설명하는 것입니다. 이론적으로는 미리 작성된 기능으로 쉽게 수행 할 수 있지만 예제의 목적을 잃을 것입니다.
어쨌든, 나는 가치를 유지하는 방법을 알아내는 데 어려움을 겪고 있습니다. 이것은 함수형 프로그래밍이므로 변수를 변경할 수 없습니다. 필연적으로 이것을 코딩해야한다면 다음과 같이 보일 것입니다.
(다음은 모두 의사 코드입니다)
f(x,y) {
int z = x;
for(int i = 0, i < y; i++){
x = x * z;
}
return x;
}
함수형 프로그래밍에서는 확실하지 않았습니다. 이것이 내가 생각해 낸 것입니다.
f(x,y,z){
if z == 'null',
f(x,y,x);
else if y > 1,
f(x*z,y-1,z);
else
return x;
}
이게 옳은 거니? z
두 경우 모두 값을 보유해야 하지만 함수 프로그래밍 에서이 작업을 수행하는 방법을 잘 모르겠습니다. 이론적으로는 내가 한 방식으로 작동하지만 그것이 옳은지 확실하지 않았습니다. 더 좋은 방법이 있습니까?
답변
우선, “빛을보고”축하합니다. 시야를 넓혀 소프트웨어 세계를 더 나은 곳으로 만들었습니다.
둘째, 함수형 프로그래밍을 이해하지 못하는 교수가 코드에 대해 유용한 정보를 말할 수있는 방법은 없습니다. 대부분의 웹 개발은 HTML / CSS / JavaScript를 사용하여 수행되므로 웹 개발 과정에서 그렇게 놀라운 것은 아닙니다. 실제로 웹 개발 학습에 얼마나 관심이 있는지에 따라 교수가 가르치는 도구를 배우려는 노력을 기울이고 싶을 수도 있습니다.
명시된 질문을 해결하기 위해 : 명령형 코드가 루프를 사용하는 경우 기능 코드가 재귀 될 가능성이 있습니다.
(* raises x to the power of y *)
fun pow (x: real) (y: int) : real =
if y = 1 then x else x * (pow x (y-1))
이 알고리즘은 실제로 명령 코드와 거의 동일합니다. 실제로, 위의 루프를 반복 재귀 프로세스의 구문 설탕으로 간주 할 수 있습니다.
부수적으로 z
, 실제로 명령형 코드 나 기능 코드 중 하나 의 값이 필요하지 않습니다 . 명령 함수를 다음과 같이 작성해야합니다.
def pow(x, y):
var ret = 1
for (i = 0; i < y; i++)
ret = ret * x
return ret
변수의 의미를 바꾸는 대신에 x
.
답변
이것은 실제로 가든 헤드의 답변에 대한 부록이지만,보고있는 패턴의 이름이 접 히고 있음을 지적하고 싶습니다.
함수형 프로그래밍에서 접기 는 각 작업 사이의 값을 “기억하는”일련의 값을 결합하는 방법입니다. 숫자 목록을 반드시 추가하십시오.
def sum_all(xs):
total = 0
for x in xs:
total = total + x
return total
우리는 값 목록 수행 xs
하고 , 초기 상태 의 0
(의해 표현 total
이 경우에는). 그런 다음 각 x
in 에 대해 일부 결합 작업 (이 경우 추가) 에 따라 xs
해당 값을 현재 상태 와 결합 하고 결과를 새 상태 로 사용합니다 . 본질적 으로와 같습니다 . 이 패턴 은 함수를 인수로받는 함수 인 고차 함수 로 추출 할 수 있습니다 .sum_all([1, 2, 3])
(3 + (2 + (1 + 0)))
def fold(items, initial_state, combiner_func):
state = initial_state
for item in items:
state = combiner_func(item, state)
return state
def sum_all(xs):
return fold(xs, 0, lambda x y: x + y)
이 구현 fold
은 여전히 필수적이지만 재귀 적으로 수행 할 수도 있습니다.
def fold_recursive(items, initial_state, combiner_func):
if not is_empty(items):
state = combiner_func(initial_state, first_item(items))
return fold_recursive(rest_items(items), state, combiner_func)
else:
return initial_state
폴드로 표현하면 기능은 다음과 같습니다.
def exponent(base, power):
return fold(repeat(base, power), 1, lambda x y: x * y))
… where repeat(x, n)
의 n
사본 목록을 리턴합니다 x
.
많은 기능, 특히 함수형 프로그래밍에 적합한 언어는 표준 라이브러리에서 접기를 제공합니다. Javascript조차도 이름으로 제공합니다 reduce
. 일반적으로 재귀를 사용하여 어떤 종류의 루프에서 값을 “기억”하는 경우 접을 수 있습니다.
답변
이것은지도와 접힘을 설명하는 데 도움이되는 보충 답변입니다. 아래 예에서는이 목록을 사용하겠습니다. 이 목록은 변경할 수 없으므로 변경되지 않습니다.
var numbers = [1, 2, 3, 4, 5]
예제에서 숫자를 사용하면 코드를 쉽게 읽을 수 있으므로 사용할 것입니다. 폴드는 전통적인 명령형 루프가 사용될 수있는 모든 것에 사용될 수 있습니다.
지도는 무언가의 목록 및 기능을 소요하고 기능을 사용하여 수정 된 목록을 반환합니다. 각 항목은 함수에 전달되고 함수가 반환하는 모든 항목이됩니다.
가장 쉬운 예는 목록의 각 숫자에 숫자를 추가하는 것입니다. 의사 코드를 사용하여 언어에 관계없이 사용합니다.
function add-two(n):
return n + 2
var numbers2 =
map(add-two, numbers)
를 인쇄 numbers2
하면 [3, 4, 5, 6, 7]
각 요소에 2가 추가 된 첫 번째 목록이 표시됩니다. 사용할 함수 add-two
가 주어졌습니다 map
.
접기 기능은 두 가지 인수를 가져와야 한다는 점을 제외하면 비슷합니다. 첫 번째 인수는 일반적으로 누산기 (왼쪽 접힘)가 가장 일반적입니다. 누산기는 루핑하는 동안 전달되는 데이터입니다. 두 번째 인수는 목록의 현재 항목입니다. map
기능에 대해서는 위와 같습니다 .
function add-together(n1, n2):
return n1 + n2
var sum =
fold(add-together, 0, numbers)
인쇄 sum
하면 숫자 목록의 합계가 표시됩니다. 15.
다음과 같은 주장을 fold
해야합니다.
-
이것이 우리가 접는 기능입니다. 접기는 함수에 현재 누산기와 목록의 현재 항목을 전달합니다. 함수가 반환하는 것이 무엇이든 새로운 누산기가되고 다음에 함수에 전달됩니다. FP 스타일을 반복 할 때 값을 “기억”하는 방법입니다. 나는 2 개의 숫자를 취해 더하는 함수를주었습니다.
-
이것은 초기 누산기입니다. 목록의 항목이 처리되기 전에 누산기가 시작되는 것. 숫자를 합산 할 때 숫자를 더하기 전에 총계는 얼마입니까? 0, 두 번째 인수로 전달했습니다.
-
마지막으로지도와 마찬가지로 처리 할 숫자 목록도 전달합니다.
접힌 부분이 여전히 이해가되지 않는다면 이것을 고려하십시오. 당신이 쓸 때 :
# Notice I passed the plus operator directly this time,
# instead of wrapping it in another function.
fold(+, 0, numbers)
기본적으로 전달 된 함수를 목록의 각 항목 사이에 놓고 왼쪽 또는 오른쪽에 초기 누적기를 추가합니다 (왼쪽 또는 오른쪽 접기의 경우에 따라 다름).
[1, 2, 3, 4, 5]
된다 :
0 + 1 + 2 + 3 + 4 + 5
^ Note the initial accumulator being added onto the left (for a left fold).
15와 같습니다.
map
한 목록을 같은 길이의 다른 목록으로 바꾸려면를 사용하십시오 .
를 사용 fold
하면 번호 목록을 합산처럼, 하나의 값으로 목록을 설정하고자 할 때.
@Jorg가 주석에서 지적했듯이 “단일 값”은 숫자와 같은 단순한 것이 될 필요는 없습니다. 목록이나 튜플을 포함한 단일 객체 일 수 있습니다! 제가 실제로 접기를 클릭하는 방식은 접기의 관점 에서지도를 정의하는 것이 었습니다 . 누산기가 어떻게 목록인지 확인하십시오.
function map(f, list):
fold(
function(xs, x): # xs is the list that has been processed so far
xs.add( f(x) ) # Add returns the list instead of mutating it
, [] # Before any of the list has been processed, we have an empty list
, list)
솔직히 말해서, 일단 각각을 이해하면 거의 모든 루핑이 접기 또는 맵으로 대체 될 수 있음을 알게 될 것입니다.
답변
기본 제공 기능으로는 해결할 수없는 좋은 문제를 찾기가 어렵습니다. 그리고 그것이 내장되어 있다면, 언어 x에서 좋은 스타일의 예가되어야합니다.
예를 들어 haskell에서는 (^)
Prelude에 이미 기능 이 있습니다.
또는 더 프로그래밍 방식으로 수행하려는 경우 product (replicate y x)
내가 말하는 것은 당신이 제공하는 기능을 사용하지 않으면 스타일 / 언어의 장점을 보여주기가 어렵다는 것입니다. 그러나 장면 뒤에서 어떻게 작동하는지 보여주는 좋은 단계 일 수 있지만 사용중인 언어에 관계없이 가장 좋은 방법을 코딩 한 다음 필요한 경우 진행 상황을 이해하도록 도와야한다고 생각합니다.