如果你有一个算法需要像(在C中)那样的东西:
int pts[length];
for(int i = 0; i < length; ++i){
for(int j = 0; j < length; ++j){
if(pts[i] == pts[j]){
//modify both pts[i] and pts[j] somehow
}
}
}
您如何将其转化为功能风格?这意味着它返回一个数组或带有修改的点列表而不更改原始数据。可以使用嵌套递归,贴图/过滤器等,循环或其他类型的Racket样式来演示答案。虽然我在Racket中尝试这个,但是我可以接受其他语言的答案。
答案 0 :(得分:1)
这里首先要说的是 算法在命令式风格中更有意义,大多数函数式语言为您提供了执行此操作所需的机制。您使用Racket作为示例,将此代码转换为执行变异的嵌套循环(例如使用racket的for
)是完全合理的。
然而,还有一些算法在功能风格中具有完美的意义;
对此的回答很大程度上取决于modify both pts[i] and pts[j]
某种方式。
让我们做点什么;假设我在派对上有一系列男孩,每个人都穿着一件衣服,如果其中两个穿着同样的衣服,他们的快乐就会减少。这是我头脑中的第一件事,但老实说它很好地概括了。
执行此操作的一种方法是使用嵌套循环,如您所述。为了在功能上做到这一点,我想我可能会这样写:
#lang racket
;; a boy is a structure: (make-boy symbol number)
(define-struct boy [dress-color happiness]
#:transparent)
;; given a list of boys,
;; decrease each boy's happiness
;; by 3 for each other boy wearing the same color
;; dress
;; list-of-boys -> list-of-boys
(define (oh-noes boys)
(oh-noes-helper boys (boy-colors boys)))
;; given a list of boys and a list of all the colors
;; decrease each boy's happiness
;; by 3 for each other boy wearing the same color
;; dress
;; list-of-boys list-of-colors -> list-of-boys
(define (oh-noes-helper boys all-colors)
(cond [(empty? boys) empty]
[else (cons (adjust-boy (first boys) all-colors)
(oh-noes-helper (rest boys) all-colors))]))
;; given a boy and a list of all the dress colors,
;; decrease the boy's happiness by three for every
;; *other* boy wearing the same color dress
;; boy list-of-colors -> boy
(define (adjust-boy boy all-colors)
(make-boy (boy-dress-color boy)
(- (boy-happiness boy)
(* 3 (sub1 (num-occurrences (boy-dress-color boy)
all-colors))))))
;; given a list of boys, return a list of colors
;; let's just use map...
(define (boy-colors boys)
(map boy-dress-color boys))
;; given a list of colors, return the number of occurrences of that
;; color in the list
;; too lazy, just using foldl...
(define (num-occurrences element list)
(length (filter (λ (c) (equal? element c)) list)))
(require rackunit)
(check-equal? (boy-colors (list (make-boy 'blue 13)
(make-boy 'green 15)
(make-boy 'orange 9)
(make-boy 'green 2)
(make-boy 'orange 2)
(make-boy 'green 1)))
(list 'blue 'green 'orange 'green 'orange 'green))
(check-equal? (num-occurrences 'green
(list 'blue 'green 'orange
'green 'orange 'green))
3)
(check-equal? (oh-noes (list (make-boy 'blue 13)
(make-boy 'green 15)
(make-boy 'orange 9)
(make-boy 'green 2)
(make-boy 'orange 2)
(make-boy 'green 1)))
(list (make-boy 'blue 13)
(make-boy 'green 9)
(make-boy 'orange 6)
(make-boy 'green -4)
(make-boy 'orange -1)
(make-boy 'green -5)))
请注意,我用非常HtDP的风格写了这个;如果你让自己自由使用Racket,你可以在几行内做到这一点。
这与原始实现相比如何?好吧,他们都有n ^ 2 运行时间。如果你想改进这个,你可能想要创建一个小的 哈希表将颜色映射到命令和命令中的出现次数 功能性解决方案。
以下是5 loc的全部内容:
;; once again more rackety-small
(define (oh-noes2 boys)
(define colors (map boy-dress-color boys))
(for/list ([b (in-list boys)])
(match-define (struct boy (c h)) b)
(boy c (- h (* 3 (sub1 (length (filter (λ(x)(eq? x c)) colors))))))))
答案 1 :(得分:1)
你真的不像Algol那样做。您使用哈希表 摆脱O(n²)。
;; finds and replaces duplicates in O(n) time
(define (replace-duplicates replacement-proc lst)
;; this goes through the list once
;; mapping every value into a hash
;; with their frequency
(define h
(foldl (lambda (e h)
(hash-update h e add1 0))
(hash)
lst))
;; goes through the list once more
;; and replacing the elements that has
;; a frequency over 1 with the result of
;; (replacement-proc e)
(map (lambda (e)
(if (> (hash-ref h e) 1)
(replacement-proc e)
e))
lst))
(replace-duplicates (lambda (x) 'duplicate)
'(1 2 3 4 1))
; ==> (duplicate 2 3 4 duplicate)
答案 2 :(得分:1)
如果你想用list做事情,那么:
(define (my-list-update lst idx val) ; older version of Racket doesn't have list-update
(if (= idx 0)
(cons val (rest lst))
(cons (first lst) (my-list-update (rest lst) (- idx 1) val))))
(define LENGTH 10)
(define initial-lst (build-list LENGTH (lambda (_) 0))) ; create list consisting of 0 for LENGTH entries
(define arr
(foldl
(lambda (i lst)
(foldl
(lambda (j lst) ; shadowing, change if you don't like it
(my-list-update lst i (+ (list-ref lst i) (+ i j))))
lst (range LENGTH))) ; for j = 0 -> LENGTH - 1
initial-lst (range LENGTH))) ; for i = 0 -> LENGTH - 1
上面的代码几乎等同于以下伪代码:
int arr[N] = initial_lst;
for(int i = 0; i < N; ++i){
for(int j = 0; j < N; ++j){
arr[i] += i + j; // not exactly, in Racket, we didn't mutate anything
}
}
您如何将其转化为功能风格?
嗯,“功能风格”的定义是什么?特别是,你真的想要一个数组作为输出吗?因为数组是一个可变数据结构,所以它不是纯粹的功能。这对你来说是否可以接受?
返回带有修改的数组或点列表,而不更改原始数据。
如果要在不修改原始数组的情况下输出数组。这很简单:只需将原始数组中的所有条目复制到新数组即可。然后,您可以改变新数组,做任何你想做的事情,并返回它。然而,这成了势在必行的风格。
如果您真的希望程序纯粹是功能性的,那么阵列就不适合您。使用上面示例的列表并不理想,因为更新和获取需要O(n),而数组可以在O(1)中执行。有一种更好的方法:使用Okasaki's random access list。此数据结构纯粹是功能性的,同时允许您更新O(log n)中的条目。它的实现非常简单(与AVL Tree等数据结构相比),您可以轻松地使用它代替列表。