간결한 자바스크립트?

21339 단어
두말할 나위 없이 요즘 내가 정말 좋아한다Clojure.

일상 업무에서는 아직 사용하지 않지만(아직?) 에 관해 Haskell에서 했던 것과 같은 방식으로 다른 언어가 문제에 접근하는 방식을 보고 해당 아이디어를 TypeScript에 다시 적용하는 것을 좋아합니다.

지난 주에 저는 Game of pure strategy problem 을 가지고 놀았고 Clojure에서 가능한 솔루션을 구현하려고 했습니다.

Clojurians Slack 채널에서 앞뒤로 몇 가지 좋은 피드백을 받은 후 다음과 같은 다음 상태 함수를 만들었습니다.

(def initial-state "Initial game state"
  {:first-player {:deck #{1 2 3 4 5 6 7 8 9 10 11 12 13} :score 0}
   :second-player {:deck #{1 2 3 4 5 6 7 8 9 10 11 12 13} :score 0}
   :bounty-deck #{1 2 3 4 5 6 7 8 9 10 11 12 13}})

(defn random-card-strategy [deck] (rand-nth (seq deck)))
(defn highest-card-strategy [deck] (apply max (seq deck)))
(def draw-card random-card-strategy)

(defn game-step [current-state]
  (let [{:keys [bounty-deck first-player second-player]} current-state
        drawn-card (draw-card bounty-deck)
        first-player-card (random-card-strategy (:deck first-player))
        second-player-card (highest-card-strategy (:deck second-player))
        match-winner (if (> first-player-card second-player-card) :first-player :second-player)]
    (-> current-state
        (update-in [match-winner :score] inc)
        (update-in [:bounty-deck] disj drawn-card)
        (update-in [:first-player :deck] disj first-player-card)
        (update-in [:second-player :deck] disj second-player-card))))

LISP는 일반적으로 매우 밀집된 언어로 알려져 있습니다. Clojure의 풍부한 seq 함수 및 매크로 세트도 고려하면 작은 코드 덩어리가 Clojure에서 많은 작업을 수행할 수 있습니다.

저는 (여전히) 주로 JavaScript/TypeScript 개발자이며 "동일한 최종 결과를 얻기 위해 작성할 수 있는 가장 간결한 코드는 무엇입니까?

나는 가능한 한 빨리 몇 가지 코드를 자갈로 만들었으며 이것이 내가 시작한 기준선입니다.

const initialState: State = {
  bountyDeck: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
  firstPlayer: {
    score: 0,
    deck: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
  },
  secondPlayer: {
    score: 0,
    deck: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
  }
}

const randomCardStrategy = (deck: number[]) => deck[Math.floor(Math.random() * deck.length)];
const highestCardStrategy = (deck: number[]) => Math.max.apply(null, deck);
const drawCard = randomCardStrategy;

const gameStep = (currentState: State): State => {
  const drawnCard = drawCard(currentState.bountyDeck);
  const firstPlayerCard = randomCardStrategy(currentState.firstPlayer.deck);
  const secondPlayerCard = highestCardStrategy(currentState.firstPlayer.deck);
  const matchWinner = firstPlayerCard > secondPlayerCard ? 'firstPlayer' : 'secondPlayer'

  return {
    bountyDeck: currentState.bountyDeck.splice(currentState.bountyDeck.indexOf(drawnCard), 1),
    firstPlayer: {
      deck: currentState.firstPlayer.deck.filter(c => c >== firstPlayerCard),
      score: matchWinner === 'firstPlayer' ? currentState.firstPlayer.score++ : currentState.firstPlayer.score
    },
    secondPlayer: {
      deck: currentState.secondPlayer.deck.filter(c => c >== firstPlayerCard),
      score: matchWinner === 'secondPlayer' ? currentState.firstPlayer.score++ : currentState.firstPlayer.score
    },
  }
}

이 스니펫에서 유형 정의는 실제 코드의 일부가 아니므로 생략했습니다.

여기에서 문제는 JavaScript에 변경 사항을 적용할 수 있는 훌륭한 스토리가 없다는 것이 분명하지만 Lens 라이브러리를 사용하여 더 나은 작업을 수행할 수 있습니다. monocle-ts




보일러 플레이트가 없으면 고려해야 할 최종 코드는 다음과 같습니다.




const gameStep = (currentState: State): State => {
  const drawnCard = drawCard(currentState.bountyDeck);
  const firstPlayerCard = randomCardStrategy(currentState.firstPlayer.deck);
  const secondPlayerCard = highestCardStrategy(currentState.firstPlayer.deck);
  const matchWinner = firstPlayerCard > secondPlayerCard ? "firstPlayer" : "secondPlayer";

  return pipe(
    currentState,
    matchWinner === "firstPlayer" ? incrementFirstPlayerScore : incrementSecondPlayerScore,
    removeBountyDeckCard(drawnCard),
    removeFirstPlayerDrawnCard(firstPlayerCard),
    removeSecondPlayerDrawnCard(secondPlayerCard)
  );
};


… 이는 Clojure의 대응물과 그리 멀지 않습니다.



<올>
  • JavaScript에는 기본적으로 사용할 수 있는 데이터 조작에 대한 좋은 이야기가 없습니다. 물건을 효과적으로 조작하려면 렌즈 및/또는 컬렉션 라이브러리가 필요합니다. 그래도 나쁜 코드를 작성하는 것은 변명이 아닙니다.
  • 점 표기법( a.b.c )이 없으면 구조화 및 스레딩 매크로가 Clojure에서 매우 중요해집니다. 그들 없이는 :


  • (-> structure :first-player :deck :card :somethingelse)

    다음과 같을 것입니다:
    (:somethingelse (:card (:deck (:first-player structure))))



    별로 좋지 않은 것 같아요.



    P.S: 알고 있습니다ImmutableJS . 그러나 타이핑에 대한 그들의 이야기는 monocle-ts가 제공하는 것만큼 세련되지 않았기 때문에 선택에서 일찍 제거했습니다.

    좋은 웹페이지 즐겨찾기