생성일: 2019년 12월 03일
수정일: 2023년 08월 01일

4clojure - Sequence Reductions (60)

  1. 문제
  2. 풀이
    1. 풀이 과정

문제

(= (take 5 (__ + (range))) [0 1 3 6 10])

(= (__ conj [1] [2 3 4]) [[1] [1 2] [1 2 3] [1 2 3 4]])

(= (last (__ * 2 [3 4 5])) (reduce * 2 [3 4 5]) 120)

제한 : reductions

풀이

(fn rd 
	([f x]
		(rd f (first x) (rest x)))
  
	([f value col]
		(if (empty? col) (list value)
			(cons value (lazy-seq (rd f (f value (first col)) (rest col)))))))

풀이 과정

우선 reductions의 동작을 봤는데 아래와 같이 동작을 했다

(reductions + [1 2 3])
;; (1 3 6)

결과 값을 보니 아래의 함수를 사용해서 동작하는 값과 같았다.

(map #(reduce + %) [[1] [1 2] [1 2 3]])
;; (1 3 6)

전개를 하는 식으로 풀이를 했는데 뭔가 일일히 계산 하는 방법도 있겠지만 좀 더 수학적으로 생각하는 방법으로 하고 싶다.

53번 문제를 이런 풀이로 풀었던 기억이 있어서 약간의 응용을 해봤다

(reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) [] [1 2 3 4])
;; [[1] [1 2] [1 2 3] [1 2 3 4]]

위 식과 조합으로 계산식을 만들었다.

(map #(reduce + %) (reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) [] [1 2 3 4]))
;; (1 3 6 10)

익명함수로 만들었다.

(fn [f & args]
  (map #(reduce f %) (reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) [] args)))

역시나 가변인자의 문제점이 있으므로 apply를 추가 했다.

(fn [f & args]
  (map #(reduce f %) (apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) args)))

실행을 해보니 오류가 나서 reduce를 다시 확인 해봤다

(reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) [1 2 3 4])
;; Execution error (IllegalArgumentException) at user/eval211$fn (REPL:1). Don't know how to create ISeq from: java.lang.Long

초기 값이 []이 아니기 때문에 last가 성립이 되지 않아 오류가 나오는 것이다.

long을 seq로 변환을 할수가 없다고 한다..그래서 apply도 같은 오류가 나왔던 것 같다. 결과 적으로는 아래의 apply 같은 방식으로 동작을 해야한다.

(apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) '([] [1 2 3 4]))
;; [[1] [1 2] [1 2 3] [1 2 3 4]]

[]가 없이 해야 위 테스트를 전부 통과를 하는데 그럴려면 if로 확인하고 해야한다. seq?로 확인을 했으나 위 조건에 충족을 하지 않는다.

(seq? nil)
;; false
(seq? [1])
;; false

;; coll?로 해야한다.
(coll? [1])
;; true
(coll? nil)
;; false

coll로 해서 했으나 아래와 같은 애러가 나온다.

(reduce (fn [acc x] (if (not (coll? acc)) (conj [] x) (conj acc (conj (vec (last acc)) x)))) [1 2 3 4])
;; Execution error at user/eval241$fn (REPL:1). Unable to convert: class java.lang.Long to Object[]

마지막 결과에 대해서 long 형이기 때문에 []형으로 변경을 할 수가 없다고 한다...이것을 극복하기 위한 방법을 조금 더 고민을 해봐야 겠다.

빈 []로 할 수는 없으므로 값의 일부를 가져와서 하는 방식으로 생각을 해봤다

(apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) '([[1]] [2 3 4]))
;; [[1] [1 2] [1 2 3] [1 2 3 4]]

근데 acc에 대한 이중 괄호가 너무 부자연 스러워 보인다.

'([1] [2 3 4])

와 같은 형식으로 하는 방법으로 해야 제대로 모든 계산을 할 수 있을 것 같은데 좀 더 고민이 필요하다.

문제를 다시 뜯어보다보니 생각을 잘 못 하고 있는 것 같았다. 첫번째와 두번째에 대한 형태가 다르다고만 생각을 했었는데 테스트를 하다보니 나중에 계산되어야 하는 값이 따로 있었다는 것을 알게 되었다 아래 테스트를 하면서 알게 된 방법이다.

(apply conj [1] [2 3 4])
;; [1 2 3 4]

(apply * 2 [3 4 5])
;; 120

(apply + [0 1 2 3 4])
;; 10

(map #(apply conj [1] %) [[] [2] [2 3] [2 3 4]])
;; ([1] [1 2] [1 2 3] [1 2 3 4])

대략적으로 문제에 대한 계산식이 나왔다.

(map #(apply + %) (apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) '([[0]] [1 2 3 4])))
;; (0 1 3 6 10)

(map #(apply conj [1] %) (apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) '([[20191110 - 정렬,필터 개선안, 헬씨 라벨]] [2 3 4])))
;; ([1] [1 2] [1 2 3] [1 2 3 4])

(map #(apply * 2 %) (apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) '([[20191110 - 정렬,필터 개선안, 헬씨 라벨]] [3 4 5])))

;; (2 6 24 120)

식은 완성 했는데 잘못된 부분이 있는거 같아서 통과가 되진 않는다

(fn [f x & arg]
	(let [a (if (nil? arg) (first x) [])
		  b (if (nil? arg) (rest x) arg)
		  c (if (nil? arg) 0 x)]
		(map #(apply f c %) (apply reduce (fn [acc x] (conj acc (conj (vec (last acc)) x))) [a] b))))

lazy-seq 때문에 테스트를 통과하지 못하고 있다.. lazy-seq를 쓰던 어떻게 하던지 간에 (rest)에서 range에 대한 무한루프가 발생을 한다.

recursive를 이용해서 문제를 풀어야 할 것 같다.

(fn rd 
	([f x]
		(rd f (first x) (rest x)))
	([f value col]
		(if (empty? col) (list value)
			(cons value (lazy-seq (rd f (f value (first col)) (rest col)))))))
			

lazy-seq를 파악하고 공부가 필요하고, 지금까지 과정으로 봤을 때 아무리 생각을 해봐도 문제가 너무 안풀릴 것 같아 인터넷으로 찾아봤고, 약간의 변형을 거쳤다.

우선 기본적으로 lazy-seq를 사용하려면 recursive 적인 함수에서 사용을 해야한다는 것을 알았다. 그렇지 않으면 원하는데로 적용이 되지 않고, 무한루프에 빠진다는 것을 경험을 했기 때문이다.

우선 위 함수와 비슷하게 cons를 [1 2 3 4 ]를 기준으로 recursive 적용을 했을 때는 아래와 같다

(fn rd 
	([x]
		(rd (first x) (rest x)))
	([value col]
		(if (empty? col) (list value)
			(cons value (rd (first col) (rest col))))))

;; [1 2 3 4]

생각했던 것과 다른 결과가 나왔다.
([1] [1 2] [1 2 3] [1 2 3 4]) 이렇게 될 줄 알았는데 무언가 계산식이 잘못 되었다는 생각이 들었다.

생각해보니 너무 처음부터 문제를 깊게 생각했던 것 같다. 문제를 인식하는 과정에서 한꺼번에 답을 도출하려고 했던 것이 문제를 푸는데 있어서 걸림돌이 되었던 것 같다.

처음부터 [1] [1 2] [1 2 3] [1 2 3 4]) 에서 +를 하는 것이 아니라 계산하는 과정에서 f와 파라미터의 조합으로 되어야 하는데 +를 염두해두고 계산식을 만들어가다보니 이런 문제가 생긴 것 같다. 너무 1번째의 풀이에만 매몰되어 있어서 2번째에 대한 풀이를 1번째와 엮어서 생각을 하다보니 문제 였다는 것을 깨달았다.

Thread last로 정리하면 아래와 같다

(fn sequance-reductions
  ([f x]
   (->> (rest x)
        (sequance-reductions f (first x))))

  ([f value col]
   (if (empty? col) (list value)
       (->> (rest col)
            (sequance-reductions f (f value (first col)))
            (lazy-seq)
            (cons value)))))
Tags: 4clojure Today I Learn