함수형 프로그래밍 유형 도우미 함수를 구축할 때 여기에 2센트만 추가하면 됩니다.
내 경험상 약간의 관심을 갖고 약간 느리고 이해하기 다소 번거로운 지나치게 복잡한 솔루션을 구축하는 것은 너무 쉽습니다.
따라서 여기에는 내장 메서드(루프 또는 직접 내장 호출)만큼 빠르며 Expr(...) 함수를 사용할 필요가 없다는 추가 이점이 있는 맵/필터/감소에 대한 빠르고 쉬운 구현이 있습니다. 그들을 활용하기 위해.
map = Function( {inputs/* list, function*/},
/* uses a single underscore _ as the wild-card */
{__i__, __result__, __list__, _},
__result__ = {};
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
Insert Into( __result__, _ = __list__[__i__]; __function__, 1 )
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
);
);
__result__
);
reduce = Function( {inputs/*list, function, seed = 0*/},
/* uses a single underscore for current item, 'acc' for current value */
{__i__, __list__, _, acc = 0},
If( N Items( inputs ) == 3,
acc = Arg( inputs, 3 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
acc = __function__
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
)
);
acc
);
filter = Function( {inputs/*list, filter = _*/},
/* uses a single underscore _ for current item */
{__i__, __list__, __result__, _filter_, _},
__result__ = {};
If( N Items( inputs ) == 1,
_filter_ = Expr( _ )
,
_filter_ = Arg( inputs, 2 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
If( __filter__,
__result__[N Items(__result__) + 1] = _
)
)
)
,
Expr( __filter__ ), Name Expr( _filter_ )
);
);
Reverse( __result__ )
);
filter({{1, 0, 2, Empty(), 3, .}});
lst = repeat( {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}}, 10000 );
start = HP Time();
a = filter({lst, _[1] < 3} );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
reduce( {{"a", "b", "c"}, Uppercase( _ || acc ), seed = ""} );
list = Repeat( {Random Integer( 1, 100000000 )}, 50000 );
start = HP Time();
Show( reduce( {list, If( _ > acc, _, acc)} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Max( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
start = HP Time();
Show( reduce( {list, acc + _} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Sum( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
strings = Repeat( {"abcdefg"}, 1e5 ); // about a second
start = Tick Seconds();
substrings1 = map( {strings, Substr( _, 1 + 2, 1 )} );
stop = Tick Seconds();
Write( "\!n applyfunction time: ", stop - start );
start = Tick Seconds();
substrings2 = {};
For( i = N Items( strings ), i > 0, i--,
Insert Into( substrings2, Substr( strings[i], 3, 1 ), 1 )
);
stop = Tick Seconds();
Write( "\!n JSL loop time: ", stop - start );
If( substrings1 == substrings2,
Write( "\!nOK" ),
Write( "\!noops" )
);(다소 느린) 노트북에서 이것을 실행하면 다음과 같은 결과가 나타납니다.
/*<BR />My Method: 0.152577
reduce({list, If(_ > acc, _, acc)}) = 99994198;
My Method: 0.056897
Max(list) = 99994198;
Builtin Method: 0.024798
reduce({list, acc + _}) = 2495626010963;
My Method: 0.04259
Sum(list) = 2495626010963;
Builtin Method: 0.007765
applyfunction time: 0.166666666656965
JSL loop time: 0.0500000000029104
OK<BR />*/축소 함수를 사용하여 비트 목록(50,000개 항목)에서 최대 수를 찾는 데는 내장 Max() 호출보다 ~2배밖에 걸리지 않았지만 동일한 목록의 합계는 ~6배 더 느렸습니다.Map/Reduce/Filter 기능의 유연성이 추가된 점을 고려하면 꽤 좋은 수치입니다.
이 글 작성이 늦어진 점 사과드립니다. 저는 무엇보다도 준비하느라 매우 바빴습니다. Functional Data Explorer에 대한 강연 ~에 JMP 디스커버리 서밋 . 이 챌린지에 대해 직원이 아닌 제출물을 두 개만 받았기 때문에 일반 기여자들도 똑같이 열중한 것 같습니다.
5번째 챌린지 이후 총 포인트는 다음과 같습니다.
Discovery Summit에 관해 제 동료인 Stan Koprowski가 JMP 스크립팅 세션 해제 . 자세한 내용을 보려면 링크를 따르세요. 우리는 당신을 거기서 만나고 싶습니다.
Challenge 5에서는 함수의 함수인 메타함수를 작성해 달라고 요청했습니다. 나의 동기는 단일 숫자나 문자열에만 적용할 수 있는 함수를 가져와서 합성 값, 벡터 및 목록에 적용할 수 있도록 확장하는 것이었습니다. 나는 이러한 유형의 작업이 다소 정기적으로 발생하며 JSL 함수가 아직 존재하지 않을 때 데이터 테이블 수식을 사용하여 구현할 수 있는 경우가 많다는 것을 발견했습니다. 저는 데이터 테이블 외부에서 이 기능을 사용할 수 있기를 원했습니다.
항목 1과 2는 비교적 간단했습니다. 벡터로 작업할 때 기존 JSL 함수나 행렬 연산을 통해 JSL이 이미 구현한 내용을 활용하는 것이 도움이 됩니다. 중앙 절대 편차(MAD)를 계산하는 가장 간단한 방법은 한 줄의 코드입니다.
중앙값(절대값( 인벡 - 중앙값( 인벡 ) ) )
어디 인벡 입력 벡터입니다. 이 솔루션은 기본 제공 JSL 함수에서 중앙값을 올바르게 생성할 수 있는지 여부에 따라 달라집니다. 코드는 MAD의 정의를 직접 따릅니다.
절사 평균을 계산하는 것은 조금 더 까다로우며 정확한 함수 정의를 제공하는 것의 중요성에 대한 통찰력을 제공합니다(나중에 자세히 설명). 일반적인 접근 방식은 다음과 관련된 분위수 사이에 경계가 있는 값의 평균을 계산하는 것이었습니다. 낮은 % 그리고 높은 비율 , 어디 낮은 % 그리고 pct안녕하세요 Challenge 5 게시물에 정의된 인수입니다. 예를 들어:
loQ = 분위수( 낮은 % , 인벡 );
hiQ = 분위수(1- pct안녕하세요 , 인벡 );
평균( 인벡 [Loc(loQ < 인벡 < hiQ)]);
그러나 이 솔루션은 이하 낮은 % 낮은 값과 이하 pct안녕하세요 높은 가치부터. 이로 인해 다듬어진 총 비율은 다음보다 적을 수 있습니다. pc낮음 + pct안녕하세요 하지만 더 이상은 안 돼 . 이 접근 방식은 다음에서 발견된 접근 방식과 일치하지 않습니다. 분포 양쪽 꼬리를 동일하게 다듬을 뿐만 아니라 더 중요한 것은 다듬어진 총 비율이 다음과 같다는 것을 보장하는 플랫폼입니다. 적어도 요청한 금액(더 많을 수도 있음) 다음 코드는 이 정의와 일치하는 결과를 생성합니다.
loRank = 천장( pctLo *N 행( 인벡 )));
hiRank = 천장((1- pct안녕하세요 )*N 행( 인벡 )));
goodVals = (loRank < 순위( 인벡 ) <= hiRank);
goodVals`* 인벡 /Sum(goodVals);
항목 3의 경우 문자열 목록의 모든 항목에서 주어진 문자열이 나타나는 모든 항목을 찾는 함수를 찾고 있었습니다. 주어진 문자열이 "a"이고 문자열 목록이
{"calcaneoastragalar","여우","아기"}
반환되는 값은 다음과 같습니다.
{[2, 5, 9, 13, 15, 17],.,[2]}
올바르게 표현된 예는 함수를 보다 정확하게 정의하는 데 도움이 되었을 것입니다. 루핑을 사용한 내 솔루션:
listContains = Function({listIn,valueString},{i,outList,nextMat,start},
outList = Repeat({.},N Items(listIn));
For(i=1,i<=N Items(listIn),i++,
nextMat = [];
start = 1;
While(start = Contains(listIn[i],valueString,start),
nextMat = nextMat |/ start;
start+=Length(valueString);
);
If(N Row(nextMat) > 0,outList[i] = nextMat,outList[i]=.);
);
outList;
);
챌린지 1에서는 사전에서 무작위로 선택된 100만 개의 단어 목록을 처리하는 데 2~3초가 걸립니다.
나의 원동력은 적용함수 다른 프로그래밍 언어에서 사용했던 기능을 복제하여 집계 데이터 구조(일반적으로 문자열 목록 또는 행렬)에 함수를 적용(또는 매핑)할 수 있었습니다. 나는 다음과 같은 것을 사용할 수 있기를 원했습니다.
길이( 문자열 목록 )
루프나 열 수식을 사용하는 대신 목록에 있는 각 항목의 길이를 가져옵니다.
챌린지를 게시하고 나서 입력 목록의 개별 항목이나 전체 입력 목록에 함수가 적용될 수 있는 상황 등 제가 고려하지 않은 미묘한 부분이 있다는 것을 깨달았습니다. 또한 각 목록 요소에 서로 다른 함수 인수를 적용할 수 있는지 여부에 대해서는 언급하지 않았습니다.
이를 염두에 두고 Challenge 5 항목의 의견과 일관성을 유지하면서 적용함수 나는 네 가지 인수를 제공했습니다.
목록에서 – 하나 이상의 숫자, 문자열, 벡터 또는 문자열 목록의 목록입니다.
fn – 적용할 함수의 인용된 이름 목록에서 . 이 기능은 내장되거나 사용자가 제공할 수 있습니다.
인수 목록 – 인수 목록 fn 그 이후에 나타나는 목록에서 또는 인수 목록의 목록입니다. 목록 목록이 제공되면 목록과 동일한 카디널리티를 가져야 합니다. 목록에서 .
깃발 - 1인 경우 fn 목록의 각 요소에 적용되며 전체 목록에 적용되는 경우 0입니다.
간단하지만 일반적인 접근 방식은 표현식과 루프를 사용하여 함수를 작성하는 것입니다. 의사코드에서:
If flag == 1 then
If argList is a list of lists then
For each item in listIn/argList, build an expression for fn
Else
For each item in listIn, build an expression for fn using the same items from argList
Else flag == 0
Build one expression for listIn and argList
이 접근 방식의 코드는 다음에서 찾을 수 있습니다. applyFunction(루프).JSL . @David_Burnham 재귀적 접근 방식을 제공합니다. Challenge_5_burnham.JSL . 재귀를 좋아하지 않는 사람은 우아하지만 재귀가 너무 깊어지면 문제가 발생합니다.
만들려고 노력 중 적용함수 루프 없는 기능은 상당히 어려운 것으로 나타났습니다. 가장 큰 문제는 항목 수를 알지 못한다는 것입니다. 인수 목록 선험적으로(또는 숫자가 변경되는 상황을 처리하는 것이 훨씬 더 어렵습니다).처음에는 표현식 평가를 교묘하게 사용하여 이 작업을 수행할 수 있기를 바랐습니다. 예를 들어 함수에 인수가 하나만 필요한 경우 다음이 작동합니다.
applyFunctionSimple = Function({listIn,fn,argList},{i,fnExpr,outList},
i = 1;
outList = {};
outList[1::N Items(listIn)] = Substitute(
Expr(func(in,arg)),
Expr(func),Parse(fn),
Expr(in), Expr(listIn[i++]),
Expr(arg), argList[1]
);
Eval List(outList)
);
마법의 대리자 길이 목록을 생성하는 것으로 시작합니다. N개 항목( 목록에서 ) 목록의 각 요소는 표현식입니다. fn (리스트인[i++], 인수1 ) , 어디 fn 그리고 인수1 함수 호출에 제공되며 목록에서[i++] 문자 그대로입니다. 언제 평가 목록 에 적용됩니다 아웃리스트 그러나 각각 나 별도의 순차 값이 제공되고 목록 항목이 평가되어 원하는 결과가 생성됩니다. 나는 다음에서 표현식을 사용하는 이러한 접근 방식을 배웠습니다. @joseph_morgan 누구와 함께 @drewfoglia / @EvanMcCorkle 이 주제를 다루는 훌륭한 Discovery 튜토리얼을 제공했습니다. 튜토리얼을 찾을 수 있습니다 여기 그리고 여기 .
불행히도 저는 이 접근 방식을 완전히 사용하는 방법을 알 수 없었고 비효율성과 우아함 때문에 피하려고 하는 기술에 의존해야 했습니다. 문자열 조작을 통해 일반 함수를 구축한 다음 표현식 평가를 사용하여 출력을 얻습니다. . 내 코드는 다음과 같습니다. applyFunction(루프 없음).JSL . 이 코드는 applyFunctionSimple , 정규식 표현식에서 중괄호를 제거하는 데 사용됩니다. 인수 목록 . 다행스럽게도, @Craige_Hales 순수한 표현 기반 솔루션을 제공합니다. 크레이지 5.JSL . 이후 챌린지 5의 설명 이전에 나에게 전송되었으므로 적용됩니다. fn 각 요소에 목록에서 오직. 또한 모든 함수 호출에는 동일한 인수 집합이 사용됩니다. 이 솔루션과 내 솔루션은 모두 루프 기반 솔루션(2-5X 사이)보다 속도가 느리다는 동일한 문제를 공유합니다.
이 모든 것이 이해되길 바랍니다. 그렇지 않거나 추가적인 통찰력이 있는 경우 아래에 의견을 게시해 주십시오.