나는 나의 파이프를 원한다

TL;박사


우리가 셸 스크립트에서 알게 된 파이프는 강력한 것이다
기능을 구성하는 메커니즘.
대부분의 프로그래밍은 같은 편의성을 실현할 수 있다
언어이 문장에서 나는 어떻게 소개하는지 보여줄 것이다
파이프가 당신이 가장 좋아하는 프로그래밍 환경에 들어갑니다.

뭐 공부 해요?


많은 플랫폼들이 작은 블록으로 프로그램을 구축하는 방법을 가지고 있다.많은 상황에서 이런 기능 조합을 완성하는 편리한 방법이 있다.우리는 파이프라고 할 수 있다.
이 글은 bash의 파이프라인(원인에 대한 분석에 대해 약간의 논쟁이 있음)을 논의한 후에 발표되었지만 분야는 여기에 그치지 않는다.
이것이 바로 우리가 토론하고자 하는 것이다. 파이프를 통해 연결된 작은 명령으로 구성된 bash 스크립트이다.
cat urls.txt | sed -e "s-http://--" -e "s/\\.com$//g"  | grep ".com$"
파이프 개념도 서로 다른 분야에 존재한다. 소리 처리가 좋은 예이다.
gstreamer website
조합은 대부분의 프로그래밍 언어의 주요 구동 목표이다.우리는 하나의 함수를 통해 일부 값을 쫓고, 그 반환값을 다른 함수의 입력으로 사용한다.
std::vector<std::string> report;
for(const auto &url: read_lines("urls.txt")){
    const auto [protocol, domain, path] = explode(url);
    if (domain.ends_with(".com")){
        report.push_back(domain);
    }
}
분명히 bash 파이프 문법은 쓰기와 읽기가 더 쉽다.

만약... 어떡하지...


우리는 파이프 문법의 단순성을 사용할 수 있다...
함수 프로그래밍과 수학에서 이 개념은 무점 함수 조합이라고 불린다.함수 파이프를 효과적으로 정의할 수 있습니다.
read_lines("urls.txt") \
    | explode_url \
    | tuple_get(1) \
    | filter(ends_with(".com"))

하지만어떻게


간결하게 보기 위해서, 나는 이 프레젠테이션에서 Python을 사용할 것이다.잠시 후 나는 함수 재부팅 문제를 해결하기 위해 C++ 예시 C++를 추가할 수 있습니다.
나는 무엇이 유창한 인터페이스인지 설명하기 시작할 것이다.연산자를 혼합에 다시 불러오면 우리는 좋은 파이프 구조 문법을 얻을 수 있다.

유창한 인터페이스


대부분의 프로그래밍 언어에서 작업을 객체에 연결할 수 있는 구축 방법이 있습니다.
ServerBuilder()\
  .on_address([localhost])\
  .on_port(1050)\
  .with_database(that_db)\
  .that_handles("rq1", handle_rq1)\
  .that_quits_on("quit")\
  .build()
이것은 다시 구축기를 되돌려주는 방법을 만들어서 실현합니다.

유창한 인터페이스 조작원


현재, 언어에서도 연산자를 다시 쓸 수 있다면, Golden1입니다. 클래스 Pipeline 와 파이프 연산자를 만들 수 있습니다. 추가 함수로 파이프 실례를 확장할 수 있습니다.
class Pipeline:
    def __init__(self, functions = tuple()):
        self.functions = functions

    def __or__(self, f):
        return Pipeline(self.functions + (f,))

    def __call__(self, arg):
        return functools.reduce(
            lambda r, f: f(r),  # function
            self.functions,  # iterable
            arg)  # initializer

"""pipeline starting element"""
ID = Pipeline()

테스트


이게 다인 것 같습니다.
예를 들어 pytest 로 쉽게 프레젠테이션할 수 있습니다.본고에서 가설incdouble 함수는 각각 한 개의 값을 증가하고 배가한다.
def test_everything_starts_with_the_identity_function():
    assert all(ID(x) == x for x in (1, 2, "abcd", None))

def test_pipeline_steps_are_applied_in_order():
    pipeline = ID | inc | double
    assert pipeline(0) == (0+1) * 2
    assert pipeline(3) == (3+1) * 2

그래도...어떻게


이제 차근차근 설명해 봅시다.

파이프를 건설하다

Pipeline류는 우리가 구성하고자 하는 함수의 용기이다.그것은 그것들을 원조 self.functions 에 저장함으로써 실현된다.(겸사겸사 한마디 하자면, 나는 tuple 의 불변성이 아니라 list 을 더 좋아한다)
이 모듈은 첫 번째 대상을 추가했습니다. 우리는 그것을 구축의 시작점인 파이프로 사용할 수 있습니다. 파이프는 같은 요소만 되돌려줍니다.함수 프로그래밍 세계에서처럼 ID라고 불린다.
지금 우리 반에는 이 특별한 성원이 생겼다__or__(self, f).그것의 유일한 목적은 우리가 셸 스크립트에서 알고 있는'파이프'문법을 제공하는 것이다. p | inc | double.파이썬에서 이것은 를 통해 이루어진 것이다.
우리는 같은 기능을 수행하기 위해 사용자 정의 이름("and_then")을 만들 수 있습니다.
    def and_then(self, f):
      return Pipeline(self.functions + (f,))

...
ID.and_then(double).and_then(inc)
단, __or__ 구성원 이름으로 파이썬에게 Pipeline 대상이 함수에 전달/파이프로 전달될 때 사용하고자 합니다.

조작원 과부하 파이프 호출


마찬가지로 특수한 구성원이 하나 더 있다. __call__ 함수.너는 짐작할 수 있지만, 이것이 바로 대상을 함수로 표현하는 원인이다.
나는 이미 functools.reduce를 사용하여 그것을 실현했지만, 당신은 수동으로 순환을 작성하여 첫 번째 파라미터를 첫 번째 함수에 전달하고, 되돌아오는 값을 다음 함수에 전달할 수 있습니다.
여기서도 우리는 그것의 다른 이름을 부를 수 있다. 예를 들면 invoke_with.비특수 구성원 파이프는 다음과 같아야 합니다.
ID.and_then(inc).and_then(double).invoke_with(10)
그러나 선택__call__은 Python이 괄호를 사용할 때 이 방법을 선택하도록 알려줍니다.
(ID | inc | double)(10)

첫 번째 매개변수 주입


내가 정말 쓰고 싶은 것은:

twentytwo = echo(10) | inc | double
fourty_eight = echo("0x18") >> from_hex | double
따라서 우리는 일을 반전시키기 위해 또 다른 기교 조수류가 필요하다.
class WithArg:
    def __init__(self, value):
        self.value = value
    def __call__(self, p: Pipeline):
        return p(self.value)
이제 쓸 수 있어요.
WithArg(10)(ID | inc | double) == (10+1) * 2
만약 우리가 | 연산자를 포기하기를 원한다면, 우리도 괄호를 제거할 수 있다.연산자 우선 순위 때문입니다.
assert 1 + 2*3 == 7
assert 1 + 2 * 3 == 1 + (2*3)
assert True | False&True == False
assert True | False&True == True | (False&True)
따라서 우리는 곱셈 연산자를 사용하여 합성할 수 있으며, 매개 변수 주입에 오른쪽 이동을 사용할 수 있다.
class Pipeline:
    ...
    def __mul__(self, f):
        return self | f

class WithArg:
    ...
    def __rshift__(self, p: Pipeline):
        return p(self.value)

assert WithArg(10) >> ID * inc * double == (10+1) * 2

한계성


본고의 코드를 사용하면 구축할 수 있는 파이프는 좋지만 셸에서 사용하는 파이프의 중요한 부분이 부족합니다.이것은 구조를 깨는 것과 관련이 있다.
참조 :
function top5 {
   grep "page" | awk '{print $5 " " $3 " " $9}' | sort -n | tail -5
}
우리가 여기서 본 것은 선에 기초한 grepawk이다.하지만 다음은 sort.이게 뭐가 달라요?응, 출력을 생성하기 전에 모든 입력을 삼킬 거야.
python 파이프는 자물쇠 단계에서 데이터를 받아들이고 되돌려주는 함수를 조합할 수 있도록 합니다.ID | grep("page") | print_elements(5, 3, 9) 개별 매개변수를 처리하여 개별 값을 생성합니다.우리는 어떻게 이런 국면에서 벗어나야 합니까?일부 파이프는 출력(또는 흐름)을 생성하기 위해 입력을 버퍼링할 수 있어야 합니다.입력이 정지되었을 때.
사실상 텍스트 기반 처리에는 두 가지 이벤트가 있는데 그것이 바로 줄 바꾸기와 파일 끝이다.사실상 모든 명령행 흐름 처리 도구는 버퍼/블록 부분과 블록 처리 부분으로 구성되어 있다.우리는 이러한 지식을 이용하여 우리의 파이프를 더욱 지능적으로 할 수 있다.근데 이 댓글에

이 파이프 결론


현재, 언어는 당신이 자신의 구조 집합을 구축할 수 있도록 합니다.우리는 그것을 이용하여 이미 알고 있는 영역에서 온 기호를 모의할 수 있다.
비록 완벽한 언어는 없지만, 우리는 충분히 가까워져서 유용하게 변할 수 있다.

좋은 웹페이지 즐겨찾기