문제

방금 끝났어요 스칼라로 프로그래밍하기, Scala 2.7과 2.8 사이의 변경 사항을 조사해 왔습니다.가장 중요해 보이는 것은 연속 플러그인인데, 그것이 무엇이 유용한지, 어떻게 작동하는지 이해가 되지 않습니다.비동기 I/O에 적합하다는 것을 보았지만 왜 그런지는 알 수 없었습니다.이 주제에 관해 가장 인기 있는 자료는 다음과 같습니다.

Stack Overflow에 대한 질문은 다음과 같습니다.

불행히도 이러한 참조 중 어느 것도 연속이 무엇인지 또는 Shift/Reset 기능이 수행해야 하는 작업을 정의하려고 시도하지 않았으며 이를 수행하는 참조를 찾지 못했습니다.링크된 기사의 예제가 어떻게 작동하는지(또는 무엇을 하는지) 추측할 수 없었기 때문에 도움이 되는 한 가지 방법은 해당 샘플 중 하나를 한 줄씩 살펴보는 것입니다.세 번째 기사에 나온 이 간단한 내용도 다음과 같습니다.

reset {
    ...
    shift { k: (Int=>Int) =>  // The continuation k will be the '_ + 1' below.
        k(7)
    } + 1
}
// Result: 8

결과가 8인 이유는 무엇입니까?그러면 아마도 내가 시작하는 데 도움이 될 것입니다.

도움이 되었습니까?

해결책

나의 블로그 무엇을 설명합니다 reset 그리고 shift 그렇습니다. 다시 읽고 싶을 수도 있습니다.

내 블로그에서도 지적한 또 다른 좋은 소스는 Wikipedia 항목입니다. 계속 통과 스타일. 그것은 스칼라 구문을 사용하지 않지만 연속은 명시 적으로 통과되지만 주제에서 가장 분명하다.

내가 블로그에서 링크하지만 깨진 것으로 보이는 구분 된 연속에 관한 논문은 많은 사용의 예를 제공합니다.

그러나 나는 가장 좋은 예라고 생각합니다 개념 구분 된 연속은 Scala Swarm입니다. 그 안에 도서관 중지 한 시점에서 코드를 실행하고 나머지 계산은 계속됩니다. 그런 다음 라이브러리는 무언가를 수행합니다.이 경우 계산을 다른 호스트로 전송하고 결과 (액세스 된 변수의 값)를 중지 된 계산으로 리턴합니다.

이제 Scala 페이지의 간단한 예조차 이해하지 못합니다. 하다 내 블로그를 읽으십시오. 나는 그 안에 결과가 왜 8.

다른 팁

기존 설명이 내가 원하는 것보다 개념을 설명하는 데 덜 효과적이라는 것을 알았습니다. 나는 이것이 명확하기를 바랍니다.

연속 기능이있을 때 cf 불러옵니다 :

  1. 실행은 나머지 부분에 걸쳐 건너 뜁니다 shift 막히고 끝에서 다시 시작합니다
    • 매개 변수가 전달되었습니다 cf 무엇입니다 shift 실행이 계속 될 때 "평가"를 차단합니다. 이것은 모든 호출마다 다를 수 있습니다 cf
  2. 실행은 끝날 때까지 계속됩니다 reset 블록 (또는 전화 할 때까지 reset 블록이없는 경우)
    • 결과 reset 블록 (또는 매개 변수) reset() 블록이없는 경우) cf 보고
  3. 실행은 계속됩니다 cf 끝까지 shift 차단하다
  4. 실행은 끝날 때까지 건너 뜁니다 reset 블록 (또는 재설정 요청?)

이 예에서는 A에서 Z에서 Z까지의 글자를 따르십시오.

reset {
  // A
  shift { cf: (Int=>Int) =>
    // B
    val eleven = cf(10)
    // E
    println(eleven)
    val oneHundredOne = cf(100)
    // H
    println(oneHundredOne)
    oneHundredOne
  }
  // C execution continues here with the 10 as the context
  // F execution continues here with 100
  + 1
  // D 10.+(1) has been executed - 11 is returned from cf which gets assigned to eleven
  // G 100.+(1) has been executed and 101 is returned and assigned to oneHundredOne
}
// I

이 인쇄물 :

11
101

표준의 예를 감안할 때 연구 논문 Scala의 구분 된 연속성의 경우 약간 수정하여 기능 입력 shift 이름이 주어집니다 f 따라서 더 이상 익명이 아닙니다.

def f(k: Int => Int): Int = k(k(k(7)))
reset(
  shift(f) + 1   // replace from here down with `f(k)` and move to `k`
) * 2

Scala 플러그인은이 예제를 계산에 따라 변환합니다 (입력 인수 내에 reset) 각각에서 시작합니다 shift 호출에 reset ~이다 교체 기능이 있습니다 (예 : f) 입력 shift.

대체 계산은입니다 바뀌었다 (즉,) 함수로 이동했습니다 k. 함수 f 함수를 입력합니다 k, 어디 k 포함 대체 계산, k 입력 x: Int, 및 계산 k 대체 shift(f) ~와 함께 x.

f(k) * 2
def k(x: Int): Int = x + 1

다음과 같은 효과가 있습니다.

k(k(k(7))) * 2
def k(x: Int): Int = x + 1

유형에 유의하십시오 Int 입력 매개 변수의 x (즉, 유형의 서명 k) 입력 매개 변수의 유형 서명에 의해 주어졌습니다. f.

또 다른 빌린 개념적으로 동등한 추상화를 가진 예, 즉 read 함수 입력입니다 shift:

def read(callback: Byte => Unit): Unit = myCallback = callback
reset {
  val byte = "byte"

  val byte1 = shift(read)   // replace from here with `read(callback)` and move to `callback`
  println(byte + "1 = " + byte1)
  val byte2 = shift(read)   // replace from here with `read(callback)` and move to `callback`
  println(byte + "2 = " + byte2)
}

나는 이것이 논리적으로 동등한 것으로 번역 될 것이라고 믿는다.

val byte = "byte"

read(callback)
def callback(x: Byte): Unit {
  val byte1 = x
  println(byte + "1 = " + byte1)
  read(callback2)
  def callback2(x: Byte): Unit {
    val byte2 = x
    println(byte + "2 = " + byte1)
  }
}

나는 이것이이 두 예를 이전에 발표함으로써 다소 난독 화 된 일관된 공통 추상화를 설명하기를 바랍니다. 예를 들어, 표준 첫 번째 예제는 연구 논문 내 이름 대신 익명 기능으로 f, 따라서 일부 독자들에게는 그것이 read 에서 빌린 두 번째 예.

따라서 구분 된 연속은 "당신은"당신은 나를 외부에서 부르기 위해 통제 통제의 환상을 만듭니다. reset"나에게"나는 당신을 안으로 불러냅니다 reset".

리턴 유형에 유의하십시오 f 하지만 k 반환 유형과 동일해야합니다. reset, 즉 f 리턴 유형을 자유롭게 선언 할 자유가 있습니다 k ~하는 한 f 같은 유형을 반환합니다 reset. ditto for read 그리고 capture (또한보십시오 ENV 아래에).


구분 된 연속은 상태의 통제를 암시 적으로 반전하지 않습니다. read 그리고 callback 순수한 기능이 아닙니다. 따라서 발신자는 참조적으로 투명한 표현을 만들 수 없으므로 의도 된 명령 적 의미론에 대한 선언 (일명 투명) 제어.

구분 된 연속으로 순수한 기능을 명시 적으로 달성 할 수 있습니다.

def aread(env: ENV): Tuple2[Byte,ENV] {
  def read(callback: Tuple2[Byte,ENV] => ENV): ENV = env.myCallback(callback)
  shift(read)
}
def pure(val env: ENV): ENV {
  reset {
    val (byte1, env) = aread(env)
    val env = env.println("byte1 = " + byte1)
    val (byte2, env) = aread(env)
    val env = env.println("byte2 = " + byte2)
  }
}

나는 이것이 논리적으로 동등한 것으로 번역 될 것이라고 믿는다.

def read(callback: Tuple2[Byte,ENV] => ENV, env: ENV): ENV =
  env.myCallback(callback)
def pure(val env: ENV): ENV {
  read(callback,env)
  def callback(x: Tuple2[Byte,ENV]): ENV {
    val (byte1, env) = x
    val env = env.println("byte1 = " + byte1)
    read(callback2,env)
    def callback2(x: Tuple2[Byte,ENV]): ENV {
      val (byte2, env) = x
      val env = env.println("byte2 = " + byte2)
    }
  }
}

명백한 환경 때문에 이것은 시끄럽게됩니다.

접선 적으로, Scala는 Haskell의 글로벌 타입 추론을 가지고 있지 않으므로 내가 아는 한 State Monad 's에 대한 암시 적 리프팅을 지원할 수없는 한 unit Haskell의 글로벌 (Hindley-Milner) 타입 추론은 다이아몬드 다중 가상 상속을 지원하지 않습니다.

연속은 나중에 호출할 계산 상태를 캡처합니다.

시프트 표현식을 떠나는 것과 재설정 표현식을 함수로 떠나는 것 사이의 계산을 생각해 보십시오.Shift 표현식 내에서 이 함수는 k라고 하며 연속입니다.한 번 이상 전달하고 나중에 호출할 수 있습니다.

재설정 표현식에서 반환된 값은 => 뒤의 Shift 표현식 내부 표현식의 값이라고 생각하는데 이에 대해서는 잘 모르겠습니다.

따라서 연속을 사용하면 다소 임의적이고 로컬이 아닌 코드 조각을 함수로 마무리할 수 있습니다.이는 코루틴 또는 역추적과 같은 비표준 제어 흐름을 구현하는 데 사용될 수 있습니다.

따라서 연속은 시스템 수준에서 사용해야 합니다.애플리케이션 코드에 이를 뿌리는 것은 악몽을 낳는 확실한 방법이 될 것입니다. goto를 사용하는 최악의 스파게티 코드보다 훨씬 더 나쁠 것입니다.

부인 성명: 나는 Scala의 연속성에 대해 깊이 이해하지 못했습니다. 단지 예제를 보고 Scheme의 연속성을 아는 것에서 추론했습니다.

내 관점에서, 최고의 설명은 다음과 같습니다. http://jim-mcbeath.blogspot.ru/2010/08/delimited-continuations.html

예 중 하나 :

제어 흐름을 좀 더 명확하게 보려면이 코드 스 니펫을 실행할 수 있습니다.

reset {
    println("A")
    shift { k1: (Unit=>Unit) =>
        println("B")
        k1()
        println("C")
    }
    println("D")
    shift { k2: (Unit=>Unit) =>
        println("E")
        k2()
        println("F")
    }
    println("G")
}

위의 코드가 생성하는 출력은 다음과 같습니다.

A
B
D
E
G
F
C

Scala 연속성에 대한 또 다른 (최근 - 2016년 5월) 기사는 다음과 같습니다.
"스칼라의 시간여행:스칼라의 CPS(스칼라의 연속)" 에 의해시반시 스리바스타바(shiv4nsh).
그것은 또한 다음을 가리킨다. 짐 맥베스'에스 기사 에서 언급된 드미트리 베스파로프'에스 답변.

하지만 그 전에는 Continuation을 다음과 같이 설명합니다.

연속은 컴퓨터 프로그램의 제어 상태를 추상적으로 표현한 것입니다..
따라서 실제로 의미하는 것은 프로세스 실행의 특정 지점에서 계산 프로세스를 나타내는 데이터 구조라는 것입니다.생성된 데이터 구조는 런타임 환경에 숨겨지는 대신 프로그래밍 언어로 액세스할 수 있습니다.

더 자세히 설명하기 위해 가장 고전적인 예 중 하나를 들 수 있습니다.

부엌의 냉장고 앞에서 샌드위치를 ​​생각하고 있다고 가정해 보세요.바로 거기에서 계속해서 주머니에 넣습니다.
그런 다음 냉장고에서 칠면조 고기와 빵을 꺼내 샌드위치를 ​​만들어서 지금 카운터에 올려두세요.
당신은 주머니에서 계속을 불러내었고, 당신은 다시 냉장고 앞에 서서 샌드위치에 대해 생각하고 있는 자신을 발견했습니다.그런데 다행히 카운터에 샌드위치가 있는데, 샌드위치를 ​​만드는 데 사용된 재료가 모두 없어졌어요.그래서 당신은 그것을 먹습니다.:-)

이 설명에서는 sandwich 의 일부입니다 프로그램 데이터 (예: 힙에 있는 객체) "를 호출하는 대신make sandwich” 일상생활을 하고 돌아오자 그 사람은 “make sandwich with current continuation” 루틴은 샌드위치를 ​​생성한 다음 실행이 중단된 부분부터 계속됩니다.

즉, 에서 발표한 바와 같이 Scala 2.11.0-RC1용 2014년 4월

우리는 다음 모듈을 담당할 관리자를 찾고 있습니다: 스칼라 스윙, 스칼라 연속.
2.12에는 새로운 관리자가 없으면 포함되지 않습니다..
다른 모듈(scala-xml, scala-parser-combinators)은 계속 유지 관리할 예정이지만 여전히 도움을 주시면 매우 감사하겠습니다.

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