문제

좋아요, 이제 부정행위는 하지 마세요.

아니요, 실제로는 1~2분 정도 시간을 내어 시도해 보세요.

"위치"는 무엇을 합니까?

편집하다:cgrand의 제안에 따라 단순화되었습니다.

(defn redux [[current next] flag] [(if flag current next) (inc next)])

(defn positions [coll]
  (map first (reductions redux [1 2] (map = coll (rest coll)))))

자, 이 버전은 어떻습니까?

def positions(coll) {
  def (current, next) = [1, 1]
  def previous = coll[0]
  coll.collect {
    current = (it == previous) ? current : next
    next++
    previous = it
    current
  }
}

나는 Clojure를 배우고 있고 그것을 좋아합니다. 왜냐하면 나는 항상 함수형 프로그래밍을 즐겼기 때문입니다.Clojure 솔루션을 생각해 내는 데 시간이 더 오래 걸렸지만 생각하다 우아한 솔루션을 제공합니다.Groovy 솔루션은 괜찮지만 지금은 이런 유형의 명령형 프로그래밍을 발견한 시점입니다. 지루한 그리고 기계적.12년 동안 Java를 사용하면서 저는 틀에 박힌 느낌을 받았고 Clojure를 사용한 함수형 프로그래밍이 제게 필요한 도움이 되었습니다.

맞습니다, 요점을 말씀드리겠습니다.글쎄요, 솔직하게 말해서 몇 달 후에 다시 Clojure 코드를 다시 보면 이해할 수 있을지 궁금합니다.물론 나는 그것에 대해 주석을 달 수 있지만 그것을 이해하기 위해 Java 코드에 주석을 달 필요는 없습니다.

그래서 내 질문은 다음과 같습니다함수형 프로그래밍 패턴에 더 익숙해지는 문제인가요?함수형 프로그래밍 전문가들이 이 코드를 읽고 이해하기 쉽다고 생각합니까?어떤 버전이 그랬나요? 이해하기 더 쉬울까요?

편집하다:이 코드가 하는 일은 점수에 따라 플레이어의 위치를 ​​계산하는 동시에 동점인 플레이어를 추적하는 것입니다.예를 들어:


Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
도움이 되었습니까?

해결책

나는 본질적인 가독성과 같은 것이 없다고 생각합니다.익숙한 것과 익숙하지 않은 것이 있습니다.두 버전의 코드를 모두 읽을 수 있었습니다.나 역시 Groovy를 모르더라도 실제로 Groovy 버전을 더 쉽게 읽을 수 있었습니다. 왜냐하면 나 역시 C와 Java를 살펴보는 데 10년을 보냈고 Clojure를 살펴보는 데는 1년밖에 걸리지 않았기 때문입니다.그것은 언어에 대해 아무 것도 말하지 않고 단지 나에 대해 말할 뿐입니다.

마찬가지로 나는 스페인어보다 영어를 더 쉽게 읽을 수 있지만, 이는 해당 언어의 본질적인 가독성에 대해서도 아무 것도 말하지 않습니다.(스페인어는 실제로 단순성과 일관성 측면에서 두 가지 언어 중 "더 읽기 쉬운" 언어일 수 있지만 여전히 읽을 수는 없습니다.)저는 지금 일본어를 배우고 있는데 정말 힘든 시간을 보내고 있는데, 일본어를 모국어로 하는 사람들도 영어에 대해 똑같이 말합니다.

평생 동안 Java를 읽는 데 보냈다면 Java처럼 보이는 것이 그렇지 않은 것보다 읽기 쉬울 것입니다.C와 유사한 언어를 보는 것만큼 Lispy 언어를 보는 데 많은 시간을 할애할 때까지 이는 아마도 사실일 것입니다.

언어를 이해하려면 무엇보다도 다음 사항에 익숙해져야 합니다.

  • 구문([vector](list), hyphens-in-names)
  • 어휘(무엇이 reductions 평균?어떻게/어디서 ​​볼 수 있나요?)
  • 평가 규칙(함수를 객체로 취급하는 것이 작동합니까?대부분의 언어에서 오류가 발생합니다.)
  • 관용어, 같은 (map first (some set of reductions with extra accumulated values))

이 모든 것에는 배우고 내면화하는 데 시간과 연습, 반복이 필요합니다.하지만 앞으로 6개월 동안 Clojure를 많이 읽고 작성한다면 지금으로부터 6개월 후에도 Clojure 코드를 이해할 수 있을 뿐만 아니라 아마도 지금보다 더 잘 이해하게 될 것이며 어쩌면 단순화할 수도 있을 것입니다. 그것.이건 어때:

(use 'clojure.contrib.seq-utils)                                        ;;'
(defn positions [coll]
  (mapcat #(repeat (count %) (inc (ffirst %)))
          (partition-by second (indexed coll))))

1년 전에 작성한 Clojure 코드를 보면 얼마나 형편없는지 겁이 나지만 읽을 수는 있다.(Clojure 코드가 끔찍하다고 말하는 것은 아닙니다.나는 그것을 읽는 데 전혀 어려움이 없었고 전문가도 아닙니다.)

다른 팁

나는 디모데의 말에 동의합니다:추상화를 너무 많이 도입했습니다.귀하의 코드를 재작업하고 다음과 같이 끝냈습니다.

(defn positions [coll]
  (reductions (fn [[_ prev-score :as prev] [_ score :as curr]] 
                (if (= prev-score score) prev curr))
    (map vector (iterate inc 1) coll)))

귀하의 코드에 대해,

(defn use-prev [[a b]] (= a b))
(defn pairs [coll] (partition 2 1 coll))
(map use-prev (pairs coll))

다음과 같이 간단하게 리팩토링할 수 있습니다.

(map = coll (rest coll))

편집하다: 더 이상 관련이 없을 수도 있습니다.

Clojure는 나에게 복잡합니다.여기에는 이해해야 할 더 많은 추상화가 포함되어 있습니다.이는 고차 함수를 사용하는 데 따른 대가입니다. 이것이 무엇을 의미하는지 알아야 합니다.따라서 고립된 경우에는 명령형에 대한 지식이 덜 필요합니다.그러나 추상화의 힘은 결합 수단에 있습니다.모든 명령형 루프를 읽고 이해해야 하는 반면, 시퀀스 추상화를 사용하면 루프의 복잡성을 제거하고 강력한 작업을 결합할 수 있습니다.

나는 또한 Groovy 버전이 고차 기능인 실제로 맵인 수집을 사용하기 때문에 적어도 부분적으로 기능한다고 주장하고 싶습니다.그 안에도 어떤 상태가 있습니다.

Clojure 버전을 작성하는 방법은 다음과 같습니다.

(defn positions2 [coll]
  (let [current (atom 1)
        if-same #(if (= %1 %2) @current (reset! current (inc %3)))]
    (map if-same (cons (first coll) coll) coll (range (count coll)))))

이는 변경 가능한 "현재"를 사용한다는 점에서 Groovy 버전과 매우 유사하지만 next/prev 변수가 없고 대신 불변 시퀀스를 사용한다는 점에서 다릅니다.Brian이 설득력 있게 표현했듯이 가독성은 본질적인 것이 아닙니다.이 버전은 이 특별한 경우에 대해 제가 선호하는 버전이며 중간 어딘가에 있는 것 같습니다.

Clojure는 언뜻 보면 더 복잡합니다.어쩌면 더 우아할 수도 있습니다.OO는 더 높은 수준에서 언어를 보다 "관련성 있게" 만드는 결과입니다.기능적 언어는 좀 더 "알고리즘"(원시적/기본적) 느낌을 갖고 있는 것 같습니다.제가 그 순간 느꼈던 게 딱 그거예요.어쩌면 클로저 작업에 더 많은 경험이 쌓이면 상황이 바뀔 수도 있습니다.

우리는 어떤 언어가 가장 간결하거나 최소한의 코드 줄로 문제를 해결할 수 있는지에 대한 게임으로 전락하고 있는 것이 두렵습니다.

나에게 문제는 두 가지입니다.

  1. 코드가 수행하는 작업을 한눈에 파악하는 것이 얼마나 쉬운가요?이는 코드 관리자에게 중요합니다.

  2. 코드 뒤에 숨겨진 논리를 추측하는 것이 얼마나 쉬운가요?너무 장황하거나 장황한가요?.너무 간결한가요?

"모든 것을 가능한 한 단순하게 만드십시오. 그러나 단순하게 만들지 마십시오."

알베르트 아인슈타인

나 역시 Clojure를 배우고 있고 그것을 좋아하고 있다.하지만 현 개발 단계에서는 Groovy 버전이 더 이해하기 쉬웠습니다.Clojure에 대해 내가 좋아하는 것은 코드를 읽고 "아하!" 마침내 무슨 일이 일어나고 있는지 마침내 "얻으십시오".내가 무엇을 정말 즐거움은 코드를 변경하지 않고도 코드를 다른 유형의 데이터에 적용할 수 있는 모든 방법을 깨닫게 된 몇 분 후 발생하는 유사한 경험입니다.저는 Clojure에서 숫자 코드를 몇 번이나 작업했는지 셀 수 없을 만큼 많았고, 잠시 후 동일한 코드를 문자열, 기호, 위젯 등에 사용할 수 있는 방법에 대해 생각했습니다.

제가 사용하는 비유는 색상을 배우는 것입니다.빨간색을 처음 접했던 때를 기억하시나요?당신은 그것을 꽤 빨리 이해했습니다. 세상에는 빨간 것들이 다 있습니다.그러다가 마젠타라는 말을 듣고 한동안 길을 잃었습니다.그러나 조금 더 노출된 후에는 개념을 이해하고 특정 색상을 설명하는 훨씬 더 구체적인 방법을 갖게 되었습니다.개념을 내면화하고 머리 속에 좀 더 많은 정보를 담아야 하지만 결과적으로는 더 강력하고 간결한 결과를 얻게 됩니다.

Groovy는 이 문제를 해결하기 위한 다양한 스타일도 지원합니다.

coll.groupBy{it}.inject([]){ c, n -> c + [c.size() + 1] * n.value.size() }

확실히 예쁘도록 리팩토링되지는 않았지만 이해하기 너무 어렵지도 않았습니다.

나는 이것이 질문에 대한 대답이 아니라는 것을 알고 있지만 다음과 같은 테스트가 있으면 코드를 훨씬 더 잘 "이해"할 수 있습니다.

assert positions([1]) == [1]
assert positions([2, 1]) == [1, 2]
assert positions([2, 2, 1]) == [1, 1, 3]
assert positions([3, 2, 1]) == [1, 2, 3]
assert positions([2, 2, 2, 1]) == [1, 1, 1, 4]

그러면 지금부터 1년 후에 코드가 무엇을 할 것으로 예상되는지 알려줄 것입니다.여기에서 본 어떤 훌륭한 코드 버전보다 훨씬 낫습니다.

나는인가? 정말 주제를 벗어?

또 다른 점은 "가독성"이 상황에 따라 다르다고 생각합니다.누가 코드를 유지 관리할 것인지에 따라 다릅니다.예를 들어 Groovy 코드의 "기능적" 버전을 유지하려면(아무리 훌륭하더라도) Groovy 프로그래머뿐만 아니라 기능적인 Groovy 프로그래머도 필요합니다.더 관련성이 높은 다른 예는 다음과 같습니다.몇 줄의 코드로 인해 "초보자" Clojure 프로그래머가 더 쉽게 이해할 수 있다면 코드는 더 큰 커뮤니티에서 이해할 수 있기 때문에 전반적으로 더 읽기 쉬울 것입니다.코드를 파악하고 편집하기 위해 3년 동안 Clojure를 공부할 필요가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top