카테고리별 지출 합계
요구사항
8월 거래 아홉 건을 카테고리별로 합산하고, 각 카테고리가 명세서에 처음 등장한 순서대로 합계를 출력하세요. 데이터는 아래 코드에 있으며, 두 버전 모두 예상 출력 아래에 표시된 줄들을 출력해야 합니다.
예상 출력
groceries: 69 transport: 23 dining: 72
나란히 보기
RxDart
FxDart
차이가 나는 이유
그룹핑은 push 모델의 "모든 것은 스트림"이라는 약속이 비싸지는
지점입니다. RxDart의 groupBy는 맵을 반환할 수 없습니다 —
소스가 영영 끝나지 않을 수도 있으니까요 — 그래서 스트림들의
스트림을 반환합니다: 새 키마다 GroupedStream 하나.
합계를 꺼내려면 각 내부 스트림을 접고(Future), 그
future를 다시 스트림으로 끌어올리고(asStream), 결과를
flatMap으로 병합해야 합니다 — sum 하나를
둘러싼 세 겹의 배관입니다. (실용적인 rx 사용자는 스트림 전체를 가변
맵으로 접어 groupBy를 아예 피해 갈 수 있습니다 — 더
짧지만, 이 예제가 다루는 연산자를 포기하는 것이고 그룹핑은 다시
명령형이 됩니다.) 그리고 이 형태에는 날카로운 모서리가 있습니다:
flatMap 대신 asyncExpand로 접으면 프로그램이
데드락에 빠집니다. 그룹 합계를 기다리는 동안 외부 스트림을 일시
정지시키는 것이, 어느 그룹이든 닫히기 전에 완료되어야 하는 소스를
멈춰 버리기 때문입니다.
FxDart의 데이터는 구조상 유한하므로, 그룹핑에 스트림들의 스트림이
필요 없습니다: groupedBy가 처음 본 키 순서로 평범한
(key, items) 레코드를 내놓고 체인은 계속 이어지며,
sumBy가 그룹별 산수를 맡습니다. 아무것도 도착 중이지
않으므로 아무것도 미뤄지지 않습니다. 살아 있는 무한 피드라면
GroupedStream 설계가 옳은 선택입니다 — 하지만 이미 손안에 있는
명세서라면 이것은 pull 형태의 작업이고, pull 버전은 그것을 세 줄로
말합니다. 판정은 FxDart에게 갑니다.
벤치마크
N = 100
시간 무승부
최대 메모리 무승부
N = 1,000,000
시간 FxDart 승
최대 메모리 무승부
막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.