백만장자 Scala 개발자가 되는 방법

14495 단어

또는 Http4s 및 Cats 효과가 있는 간단한 웹 API



"The EuroMillions lottery gives you a 1 in 140 million chance of not going to work tomorrow. With alcohol it's 1 in 5." - Anonymous



좋아, 백만장자로부터 "감사"메시지를 받을 가능성은 없지만 이 게시물을 팔로우하면 백만장자가 될 것 같은 느낌이 듭니다. 우리 물리학자들이 옳다면. 그리고 되기의 주어진 의미를 위해.

모든 양자 얻기



여튼 구성은 이렇습니다. 우리는 양자 난수 생성기를 사용하여 우주를 1억 4천만 개 정도의 세계로 나누고...

잠깐, 우주가 갈라진다?



양자 역학에 대한 가장 엄밀하고 평이한 해석에 따르면, 우리가 확률적이라고 인식하는 모든 양자 사건은 실제로 발생하지 않습니다. 하나의 결과를 볼 수 있습니다.

따라서 모든 것을 감안할 때 우리는 양자 난수 생성기를 사용하고 모든 우주를 만들고 나(또는 당신)의 버전이 복권에 당첨될 것이라고 보장합니다.

충분한 이론, 코딩을 시작합시다



가장 먼저 한 일은 난수를 얻기 위한 서비스를 만드는 것이었습니다. RandomService 클래스는 주어진 숫자에 대해 그만큼의 난수를 생성하는 함수를 사용합니다. 계산을 즉시 평가하고 싶지 않기 때문에 계산을 래핑하기 위해 Cats Effect IO 모나드를 사용하고 있습니다. 호출은 프런트 엔드 API에 대한 것이고 차례로 백엔드 API를 호출하기를 원하므로 명시적으로 동기적으로 실행하고 싶지 않습니다.

class RandomService(randomFunction: Int => IO[Seq[Int]])


서비스에서 무작위를 생성한 다음 이를 사용하여 Fisher-Yates 셔플을 수행합니다. 1에서 50까지 5개의 숫자를 요청하면 1에서 50까지의 모든 숫자를 생성하고 그 중 5개를 가져옵니다.

def generateNumberSequence(numberGroup: NumberGroup): IO[Seq[Int]] = {
    randomFunction(numberGroup.size).map(rands => {
        val result = shuffle(1 to numberGroup.size, rands)
            .take(numberGroup.count)
        if (numberGroup.isFullList) result else result.sorted
    })
}

@tailrec
final def shuffle(values: Seq[Int], randoms: Seq[Int], index: Int = 0): Seq[Int] = {
    val n = values.size
    if (index == n - 1) values
    else shuffle(swap(values, index, index + (randoms(index) % (n - index))), randoms, index + 1)
}

@tailrec
final def swap(seq: Seq[Int], i: Int, j: Int): Seq[Int] = {
    if (i == j) seq
    else if (j < i) swap(seq, j, i)
    else seq.take(i) ++ Seq(seq(j)) ++ seq.slice(i + 1, j) ++ Seq(seq(i)) ++ seq.drop(j + 1)
}


NumberGroup은 요청된 숫자의 크기와 개수를 전달하는 데이터 유형일 뿐입니다. 그룹이 꽉 찼을 때를 제외하고 요청한 번호를 정렬합니다. 1-50의 모든 번호를 요청한 경우 순서가 중요합니다. 1,2,3 ... 50만 원하는 것이 아닙니다!

다른 흥미로운 부분은 웹 서비스입니다Lotto.

trait Lotto {
  def generate(vals: Seq[NumberGroup]): IO[Lotto.Response]
}

object Lotto {
  final case class Response(nums: Seq[(Int, Seq[Int])])
  val randomService = new RandomService(QRNG.f)
  object Response {
    implicit val lottoEncoder: Encoder[Response] = (resp: Response) =>
      Json.obj(
        ("Your Numbers:",
      Json.fromFields(resp.nums.map(t => (t._1.toString, t._2.asJson)))
        )
      )
  }

  def impl: Lotto = (vals: Seq[NumberGroup]) => {
    val resp = vals.map(randomService.generateNumberSequence).sequence
    resp.map(r => Response(r.zipWithIndex.map(t => (t._2, t._1))))
  }


저는 코드의 직관적인 선언적 특성을 좋아합니다. 또한 얼마나 간결한지. 대부분의 복잡성은 반응이 괜찮아 보이도록 하는 데 있습니다. 그래도 약간의 작업이 필요합니다!

또한 IO 모나드 시퀀스를 가져와서 하나의 IO로 시퀀스하는 .sequence 메서드에 주목하십시오. 개념적으로는 계산을 수행하지 않고 결과를 재구성하는 것입니다. 멋진!

남은 것은 Http4s 서버를 실행하는 것입니다. 이보다 더 간단할 수는 없습니다.

  def run: IO[Nothing] = {
    val lottoAlg = Lotto.impl
    val httpApp = QuantumMillionaireRoutes.lottoRoutes(lottoAlg).orNotFound
    val finalHttpApp = Logger.httpApp(logHeaders = true, logBody = true)(httpApp)
    EmberServerBuilder.default[IO]
        .withHost(ipv4"0.0.0.0")
        .withPort(port"8080")
        .withHttpApp(finalHttpApp)
        .build
  }.useForever


그리고 있습니다. 모든 코드는 내 GitHubhere에 있습니다.

그리고 당신이 백만장자가 되었을 때, 저를 그 버전으로 알려주세요. 제가 그를 안다면, 그는 그것을 그의 이력서에 넣을 것입니다...

좋은 웹페이지 즐겨찾기