; Често използвани функции за аргументите на term и next: (define (id x) x) (define (++ x) (+ x 1)) ; Главната функция-акумулатор, която позволява параметризирано ; "обхождане" на даден интервал. Можем да укажем стъпката за ; обхождане, методът за изчисление на общия член, както и ; акумулиращата операция с нейния неутрален елемент: (define (accumulate op null-value term a next b) (if (> a b) null-value (op (term a) (accumulate op null-value term (next a) next b)))) ; Тъй като accumulate прави едно просто линейно обхождане ; на интервала, можем да напишем неин итеративно-рекурсивен вариант. ; Оттук нататък можем да използваме тази версия при изчисления ; с числа/булеви стойности, рекурсивният accumulate ще ни трябва ; по-нататък при работа със списъци: (define (accum-iter op null-value term a next b) (define (helper curr result) (if (> curr b) result (helper (next curr) (op (term curr) result)))) (helper a null-value)) ; Филтриращата версия на accumulate взима избирателно числа ; от обходения интервал, преди да го зачисли към крайния резултат. ; При повечето числени операции може да се заобиколи, но е добър ; съвет да я използваме когато имаме възможността: (define (filter-accum pred? op null-value term a next b) (cond [(> a b) null-value] [(pred? a) (op (term a) (filter-accum pred? op null-value term (next a) next b))] [else (filter-accum pred? op null-value term (next a) next b)])) ; Filter-accumulate също може да се сведе до прост итерационен ; процес, в който имаме една проверка повече. Отново - можем да ; избираме тази версия пред другата, докато работим с комутативни ; операции (+,*,or,and,...): (define (filter-iter pred? op null-value term a next b) (define (helper curr result) (cond [(> curr b) result] [(pred? curr) (helper (next curr) (op (term curr) result))] [else (helper (next curr) result)])) (helper a null-value)) ; Сума на целите числа в даден целочислен интервал: (define (sum-range a b) (accum-iter + 0 id a ++ b)) ; Изчисляване на факториел чрез accumulate - тук интервалът ; за обхождане не е зададен явно в условието: (define (fact-accum n) (accum-iter * 1 id 1 ++ n)) ; Повдигане на степен чрез accumulate. Тук също "интервалът" ; е неявен. Уловката в тази задача е, че за смятане на общия ; член трябва да използваме външния аргумент x. Ако кръстим ; аргумента на term x, той ще "скрива" външния x и изчисленията ; няма да бъдат коректни. Кратко обяснение - модел на средите: (define (expt-accum x n) (define (term k) x) (accum-iter * 1 term 1 ++ n)) ; Брой делители на дадено естествено число в целочислен интервал: (define (count-divisors n a b) (define (term k) 1) (define (is-divisor? k) (= (remainder n k) 0)) (filter-iter is-divisor? + 0 term a ++ b)) ; Брой фиксирани точки на дадена функция в целочислен интервал: (define (count-fixed f a b) (define (term k) 1) (define (fixed? x) (= (f x) x)) (filter-iter fixed? + 0 term a ++ b)) ; Изчислява сумата x + 2.x^2 + 3.x^3 + ... + n.x^n: (define (powers-sum x n) (define (term k) (* k (expt x k))) (accum-iter + 0 term 1 ++ n)) ; Изчислява сумата f(0) + f(1) + ... + f(n): (define (func-sum f n) (accum-iter + 0 f 0 ++ n)) ; Изчислява биномния коефициент (n k), или ; броя комбинации на n елемента от k-ти клас. ; Тук отново е съществено как ще кръстим аргумента ; на вложената функция, тъй като искаме да използваме ; оригиналните стойности на n и k: (define (combinations n k) (define (term x) (/ (+ n (- k) x) x)) (accum-iter * 1 term 1 ++ k)) ; Проверка дали дадено число е просто чрез filter-accumulate. ; Тук можем да акумулираме булеви стойности - търсим в интервала ; [2;sqrt(n)] и, ако сме намерили на някоя стъпка "истина", тоест ; делител, то трябва да върнем истина. ; Заради ограничения в езика не можем да използваме and и or ; така лесно, както ползваме + и *, но можем да си дефинираме анонимна ; функция, която да ги използва: (define (prime-accum n) ; (and (> n 1) (= (count-divisors n 2 (sqrt n)) 0))) <- и count-divisors ползва accumulate все пак (define (divides? k) (= (remainder n k) 0)) (if (= n 1) #f (not (accum-iter (lambda (x y) (or x y)) #f divides? 2 ++ (sqrt n))))) ; Проверка дали дадено число е съвършено: (define (perfect-accum n) (define (is-divisor? k) (= (remainder n k) 0)) (= n (filter-iter is-divisor? + 0 id 1 ++ (- n 1)))) ; За приближено изчисление на интеграл използваме разбиването ; на интервала на по-фини интервали. accumulate не ни задължава ; да обхождаме интервала целочислено - това зависи само от задачата: (define (integrate f a b dx) (define (term x) (* (f x) dx)) (define (next x) (+ x dx)) (accum-iter + 0 term a next b)) ; Бонус: изчисление на същия интеграл, но по метода на трапците. ; Изчисленията са много по-точни, заради което можем да намалим ; точността dx без загуба на точност в резултата - но със значително ; увеличена производителност. (define (integrate* f a b dx) (define (term x) (* (/ (+ (f x) (f (+ x dx))) 2) dx)) (define (next x) (+ x dx)) (accum-iter + 0 term a next b))