요약


  • 메인루틴(Main routine): 메인루틴은 보통 프로그램의 시작점이며, 프로그램의 주 흐름을 담당합니다. 메인루틴은 일련의 작업을 수행하고 다른 서브루틴이나 코루틴을 호출할 수 있습니다. 프로그램이 시작되는 메인코드라고 생각하면 됩니다.
  • 서브루틴(subroutine): 메인루틴에서 호출되는 함수, 또는 서브루틴에서 호출되는 함수들을 의미합니다. 즉, 다른 루틴에서 호출되는 경우를 의미합니다.
  • 코루틴(Coroutine): 메인루틴에서 호출되지만, 코루틴은 실행되는 도중에, 일시중단되어 다시 메인루틴으로 전환되었다가 다시 코루틴으러 전환될 수 있는 "제어흐름"이 가능한 루틴을 의미합니다.

 

코루틴, 메인루틴, 서브루틴은 프로그래밍에서 중요한 개념이며, 이를 이해하는 것은 프로그래밍의 성능을 향상시키는 데 도움이 됩니다. 각각의 이유를 자세히 설명하겠습니다.

  1. 프로그램의 구조 이해(메인루틴/서브루틴의 이해):
    • 메인루틴은 프로그램의 주요 흐름을 담당하며, 프로그램의 시작점입니다. 이를 이해함으로써 프로그램이 어떻게 실행되는지 전반적으로 이해할 수 있습니다.
    • 서브루틴은 재사용 가능한 작은 조각의 코드를 나타냅니다. 이를 통해 코드를 더 모듈화하고 유지 보수하기 쉽게 만들 수 있습니다.
  2. 코드의 재사용과 모듈화(서브루틴/코루틴의 이해):
    • 서브루틴을 사용하면 비슷한 작업을 수행하는 코드를 여러 곳에서 재사용할 수 있습니다. 이로써 코드의 중복을 피하고 유지보수성을 향상시킬 수 있습니다.
    • 코루틴은 서브루틴과 유사하지만, 일시 중단되고 다시 시작될 수 있기 때문에 작업의 흐름을 제어하는 데 유용합니다. 이를 통해 비동기 작업이나 상태 유지가 필요한 작업을 효율적으로 처리할 수 있습니다.
  3. 동시성과 비동기 프로그래밍(코루틴의 의해):
    • 코루틴은 동시성과 비동기 프로그래밍에서 중요한 역할을 합니다. 코루틴을 이용하면 여러 작업을 동시에 실행하고 작업 간에 상태를 공유할 수 있습니다. 이를 통해 복잡한 동작을 효과적으로 조정하고 성능을 향상시킬 수 있습니다.
  4. 효율적인 코드 실행:
    • 코루틴은 작업의 일시 중단과 다시 시작을 가능하게 함으로써 I/O 바운드 작업을 효율적으로 처리할 수 있습니다. 이를 통해 대기 시간이 긴 작업을 실행하는 동안 다른 작업을 수행할 수 있으며, 시스템 자원을 더 효율적으로 활용할 수 있습니다.

이러한 이유들로 인해 코루틴, 메인루틴, 서브루틴을 이해하는 것은 프로그래밍에서 중요한 역할을 합니다. 이러한 개념을 잘 숙지하고 활용함으로써 보다 효율적이고 유연한 코드를 작성할 수 있습니다.

 

메인루틴(Main routine)


메인루틴은 프로그램의 주요 흐름을 담당하는 부분으로, 일반적으로 프로그램의 시작점이며 종료 지점이 될 수도 있습니다. 메인루틴은 전체 프로그램의 실행 흐름을 정의하고 조절하며, 다른 서브루틴이나 함수를 호출하여 작업을 수행하고 그 결과를 처리합니다.

메인루틴은 보통 다음과 같은 역할을 수행합니다:

  1. 프로그램의 시작점: 메인루틴은 일반적으로 프로그램이 실행되면 가장 먼저 실행되는 부분입니다. 프로그램의 시작 지점을 정의하고 초기화 작업을 수행합니다.
  2. 주요 작업 수행: 메인루틴은 프로그램이 수행해야 하는 주요 작업을 정의하고 호출합니다. 이는 사용자의 입력을 받는, 데이터를 처리하는, 다른 모듈이나 서브루틴을 호출하는 등의 작업을 포함할 수 있습니다.
  3. 서브루틴 호출: 메인루틴은 필요에 따라 다른 서브루틴이나 함수를 호출하여 작업을 분할하고 모듈화합니다. 이를 통해 코드를 재사용하고 유지보수성을 향상시킬 수 있습니다.
  4. 프로그램의 종료: 메인루틴은 프로그램이 종료되는 지점을 나타냅니다. 모든 주요 작업이 완료된 후 정리 작업을 수행하고, 필요에 따라 리소스를 해제하고 프로그램을 종료합니다.

간단한 예시로 메인루틴을 파이썬코드로 구현해보겠습니다. 아래와 같이 input으로 이름, 나이를 입력받아 출력하는 프로그램입니다. 다른 모듈에서 호출되는 것 없이 main()함수가 호출되고 종료됩니다. 이 main함수에서 프로그램이 시작되고, 종료되기에, "프로그램의 흐름이 정의"되었다라고 말할 수 있습니다. 즉, main()이 메인루틴이 됩니다.

def main():
    print("프로그램 시작")

    # 사용자 입력 받기
    name = input("이름을 입력하세요: ")
    age = int(input("나이를 입력하세요: "))

    # 정보 출력
    print("입력된 정보:")
    print("이름:", name)
    print("나이:", age)

    # 종료 메시지 출력
    print("프로그램 종료")

if __name__ == "__main__":
    main()

 

서브루틴(Subroutine): 메인루틴이나 서브루틴에서 호출되는 함수


서브루틴은 다른 프로그램에서 호출되는 (=피호출)되는 함수를 의미합니다. 예는 아래와 같습니다. 위의 예시와 같이 main()이 호출되고 종료되기에 메인루틴은 main()이라는 함수입니다. 그리고 이 main()내에서 calculate_sum()이 호출되고 종료됩니다. 즉, main인 메인루틴에 의해서 호출되는 calculate_sum이 서브루틴입니다. 

def main():
    print("메인루틴 시작")
    result = calculate_sum(3, 4)
    print("결과:", result)
    print("메인루틴 종료")

def calculate_sum(a, b): # 서브루틴
    return a + b

if __name__ == "__main__":
    main()

시간에 흐름에 따라, 두 루틴을 시각화하면 아래와 같습니다. 메인루틴이 서브루틴을 호출하고, 그 동안 메인루틴은 서브루틴이 일을 다 처리할 때까지, 잠시 작업대기를 합니다. 그리고, 서브루틴의 일이 끝나면, 메인루틴으로 다시 작업이 넘어가서 메인루틴의 테스크가 종료됩니다.

Figure 1. 시간의 흐름에 따른 메인루틴-서브루틴

 

코루틴(Coroutine): "일시중지 후 재시작"이 가능한 함수


코루틴은 일시 중단되고 다시 시작될 수 있는 함수로, 일반적인 함수와 다르게 실행 중에 일시 중단되어 값을 주고 받을 수 있습니다. 이를 통해 비동기적인 작업이나 상태를 유지하는 작업을 효율적으로 처리할 수 있습니다. 특히, 코루틴은 서브루틴과 다르게 메인루틴으로 작업상태가 넘어가도, 코루틴내 내용을 잃지않고 유지할 수 있습니다. 그리고, 이런 메인루틴->코루틴, 코루틴->메인루틴의 작업상태를 넘겨 실행하는 단계를 "진입점"이라고합니다. 이런 진입점은 서브루틴은 "메인루틴->서브루틴"의 1개의 진입점을 가지지만, 코루틴은 여러 개의 진입점을 가집니다.

 

코루틴을 사용하려면, 코루틴의 성격을 정리해보겠습니다. 

  • 코루틴은 generator의 특별한 형태입니다. (generator도 데이터를 yield와 함께 하나씩 얻어올 수 있고, 그 다음 차례가 그대로 유지되기에 코루틴으로 취급합니다.)
  • 코루틴은 파이썬의 2.5부터 generator로 설명되지만, 실제로 async, await구문과 함께쓰려면 3.5의 이상부터 필요합니다.

그리고, 코루틴의 시작/값보내기/값얻기의 구현방법을 알아야합니다.

  • 코루틴의 시작: 코루틴은 보통 while True 구문을 이용해서 계속해서 일반적으로 코루틴의 시작을 알립니다.
  • 코루틴에 값보내기: 코루틴에 값을 보내는 방법은 코루틴객체.send(객체)로 외부의 값을 코루틴으로 전달합니다.
  • 코루틴 내에서 값 얻기: 이 값을 가져오기 위해서, `yield` 키워드를 이용하여 값을 얻어옵니다.

 

아래의 예시에서는 `searcher()`가 코루틴 함수입니다. 하나 씩 내용을 확인해보겠습니다.

  1. while구문으로 코루틴을 계속 실행하는 상태로 둡니다.
  2. while 구문이 이하가 도는 경우, yield구문에서 값이 올 때까지 기다립니다.
  3. next()가 호출되어야하는 이유는 제너레티너나 코루틴을 실행하고 첫번째 값을 얻기위해 호출합니다. 코루틴은 yield 표현식이 보이면 일시중단됩니다.
  4. 코루틴객체.send(객체)의 구문으로 코루틴에 값을 보내면, 그 보내는것과 동시에 코루틴이 실행됩니다. 다시 while True 구문에 의해서 yield 키워드가 등장하고, 코루틴이 일시중지됩니다. 이와 동시에 다시 메인루틴으로 작업이 옮겨갑니다.

 

def searcher():
    """
    검색어를 받아들이고 출력하는 코루틴
    """
    while True:
        query = yield  # 일시 중단하고 외부로부터 값을 받음
        print("검색어:", query)

# 코루틴 생성
search = searcher()
next(search)  # 코루틴 시작

# 코루틴에 값 전달
search.send("파이썬")
search.send("데이터 분석")

Figure2. 메인루틴-코루틴의 시간에 엔트리포인트 예

또, 다른 예시입니다. 코루틴은 일시정지를 함으로, 코루틴 내의 그 내용을 잃어버리지 않고 유지할 수 있다고 했습니다. 코루틴에서 average, count, total라는 변수를 계속 코루틴에서 들고 있고, send로 더할 때 마다, 코루틴의 변수가 변경되는 것을 확인할 수 있습니다.

def running_average():
    """
    입력된 숫자들의 평균을 계산하는 코루틴
    """
    total = 0
    count = 0
    average = None

    while True:
        value = yield average  # 새로운 값 받기
        total += value
        count += 1
        average = total / count

# 코루틴 생성
calc_average = running_average()
next(calc_average)  # 코루틴 시작

# 코루틴에 숫자 전달하고 평균 계산
print("평균:", calc_average.send(10))  # 출력: 10.0
print("평균:", calc_average.send(20))  # 출력: 15.0
print("평균:", calc_average.send(30))  # 출력: 20.0

 

 

코루틴을 이용한 async, await의 활용


asyncio을 사용해서 비동기적으로 작업을 수행할 수 있습니다. asyncio을 사용하기위해 대표적인 키워드 2개를 살펴보겠습니다.

      • async 키워드: async def로 함수를 정의하면 해당 함수는 코루틴 함수로 선언됩니다. 이는 함수가 비동기적으로 실행될 수 있음을 나타냅니다. 아래와 같이, async def 로 정의된 함수는 인자를 담아 호출하면, coroutine object로 정의됨을 알 수 있습니다.

    • await 키워드: await 키워드는 비동기 작업이 완료될 때까지 대기하고, 해당 작업의 결과를 반환합니다. await 있는 키워드가 실행되면, 이 라인의 구문이 실행되는 동안, "비동기적으로 메인루틴으로 잡이 넘어가고", "완료되면 메인루틴의 작업에서 다시 코루틴으로 작업이 넘어옵"니다.  Q. "async함수를 정의하면 꼭 await구문을 써야하냐?"할수도있는데, 꼭 사용하지 않아도 됩니다만, 비동기작업을 안쓸꺼면 굳이 async로 정의할 필요는 없을 것 같습니다
async def hello():
    print("안녕하세요, 코루틴!")

async def main():
    print("메인 루틴 시작")
    await hello()
    print("메인 루틴 종료")

asyncio.run(main())

 

아래의 예시를 보겠습니다.

  1. `async def`로 fetch_url이라는 함수를 비동기적인 함수로 정의했습니다. 
  2. `await` 키워드를 fetch_url내에 정의합니다. ` fetch_url `이 호출되고 await구문이 있는 라인이 실행되면 sleep이 실행되면서, 작업을 메인루틴으로 넘깁니다.
  3. 메인루틴이 실행중이다가, await구문이 완료되면 그 다음인 return으로 반환됩니다.
  4. `async main`에서는 await asyncio.gather로 비동기작업을 여러개로 구성할 때, 모든 작업이 완료될 때까지 대기해주는 함수입니다. 
import asyncio

async def fetch_url(url):
    print(f"URL 가져오는 중: {url}")
    await asyncio.sleep(1)  # 비동기적으로 1초 동안 대기
    print(f"URL 가져오기 완료: {url}")
    return f"Content of {url}"

async def main():
    tasks = [fetch_url("https://www.example.com"), fetch_url("https://www.google.com")]
    completed_tasks = await asyncio.gather(*tasks)
    print("모든 작업 완료:")
    for result in completed_tasks:
        print(result)

asyncio.run(main())

 

언제쓰면 유용한가? "네트워크 요청", "file I/O bound task", "데이터베이스 쿼리"


  1. 동시성 증가: 비동기 프로그래밍을 사용하면 여러 I/O 작업을 동시에 처리할 수 있습니다. 한 작업이 완료될 때까지 기다리지 않고 다음 작업을 시작할 수 있습니다.
  2. 응답 시간 개선: I/O 바운드 작업을 동시에 처리함으로써 전체 시스템의 응답 시간을 개선할 수 있습니다. 작업이 완료될 때까지 기다리는 동안 다른 작업을 수행하여 전체 처리 시간을 최소화할 수 있습니다.
  3. 자원 효율성: 비동기 프로그래밍을 사용하면 블로킹되는 대기 시간 동안 다른 작업을 수행할 수 있으므로 시스템 자원을 더 효율적으로 활용할 수 있습니다.
  4. 스케일링 용이성: 비동기 프로그래밍을 사용하면 더 많은 동시 요청을 처리할 수 있으며, 이로써 시스템의 스케일링이 용이해집니다.
반응형

요약


FastAPI middleware은 어플리케이션의 요청 및 응답처리과정중에 중간에 위치하여, 여러 작업을 수행할 수 있는 요청->동작 사이, 응답->동작사이에 여러 기능을 수행
주로 요청 및 응답을 변형하거나(예, 암호화), 인증, 로깅및 모니터링, 예외처리등을 수행.
ASGI (Asynchronous Server Gateway Interface) 프로토콜을 따르는 웹 애플리케이션에서 비동기 요청을 처리하기 위해, request._receive속성에 설정하도록함


fastAPI의 미들웨어는 @app.middleware("http")와같이 함수위의 데코레이터로 사용가능합니다.
async def set_body(request: Request, body: bytes) 함수는 원래 요청 객체에 바이트로 된 본문을 설정하기 위해 사용됩니다. 
아래의 함수 내에서 async def receive() -> Message 함수를 정의하고, 이 함수는 request 객체의 _receive 속성으로 설정됩니다. 
따라서 ASGI 프레임워크는 요청을 수신할 때 이 _receive 함수를 호출하여 요청 객체의 본문을 설정하여 로깅할 수 있습니다.

async def set_body(request: Request, body: bytes):
    async def receive() -> Message:
        return {"type": "http.request", "body": body}

    request._receive = receive


@app.middleware("http")
async def log_request_payload(request: Request, call_next):
    payload = await request.body()
    await set_body(request, payload)
    request.app.state.logger.debug(f"Request payload: {payload.decode()}")
    response = await call_next(request)
    return response

 



아래의 call_next의 인자가 callable로 (reqeust)을다시 인자로 받아야하는데, FastAPI.reqeust은 아래와 같이 구성되어있습니다.
request._receive 속성이 property로 설정되어있고 타입이 empty_receive라 Courutine의 타입을 받게되어있습니다.
그래서 async def로 정의한 함수를 넣어주도록하고, 그 메시지의 형태를 stream내애 얻어지는 형태랑 유사하게 받도록합니다.

class Request(HTTPConnection):
    _form: typing.Optional[FormData]

    def __init__(
        self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send
    ):
        super().__init__(scope)
        assert scope["type"] == "http"
        self._receive = receive
        self._send = send
        self._stream_consumed = False
        self._is_disconnected = False
        self._form = None

    @property
    def method(self) -> str:
        return self.scope["method"]

    @property
    def receive(self) -> Receive:
        return self._receive

    async def stream(self) -> typing.AsyncGenerator[bytes, None]:
        if hasattr(self, "_body"):
            yield self._body
            yield b""
            return
        if self._stream_consumed:
            raise RuntimeError("Stream consumed")
        self._stream_consumed = True
        while True:
            message = await self._receive()
            if message["type"] == "http.request":
                body = message.get("body", b"")
                if body:
                    yield body
                if not message.get("more_body", False):
                    break
            elif message["type"] == "http.disconnect":
                self._is_disconnected = True
                raise ClientDisconnect()
        yield b""
반응형

conda 설치후 아래와 같은 에러가 발생


$ conda activate myenv
usage: conda [-h] [--no-plugins] [-V] COMMAND ...
conda: error: argument COMMAND: invalid choice: 'activate' (choose from 'clean', 'compare', 'config', 'create', 'info', 'init', 'install', 'list', 'notices', 'package', 'remove', 'uninstall', 'rename', 'run', 'search', 'update', 'upgrade', 'build', 'content-trust', 'convert', 'debug', 'develop', 'doctor', 'index', 'inspect', 'metapackage', 'render', 'skeleton', 'verify', 'pack', 'token', 'server', 'env', 'repo')

 

 

해결방법: conda 초기화가 안되어 발생하는 문제


1. Conda 업데이트: 먼저 Conda를 최신 버전으로 업데이트하십시오. 아래 명령어를 실행하여 Conda를 업데이트합니다.

$ conda update -n base -c defaults conda

 

2. 시스템 환경 변수 확인: Conda가 설치된 경로를 시스템 환경 변수에 제대로 추가했는지 확인하십시오. 다음 명령어를 사용하여 Conda의 경로를 확인하고 확인하세요. "/home/사용자/anaconda3/bin" 이러한 경로가 존재해야합니다.

$ echo $PATH
/home/<사용자>/anaconda3/bin:/home/heon/anaconda3/condabin:/home/heon/.vscode-server/bin/e7e037083ff4455cf320e344325dacb480062c3c/bin/remote-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0:/mnt/c/Windows/System32/OpenSSH:/mnt/c/Users/4pygm/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/4pygm/AppData/Local/Programs/Microsoft VS Code/bin:/snap/bin

 

3. shell 재시작

$ source ~/.bashrc  # 또는 ~/.zshrc

 

4. Conda 초기화

$ conda init

 

반응형
%load_ext autoreload
%autoreload 2
반응형

요약


np.array()와 np.asarray()는 NumPy에서 배열을 생성하는 함수입니다.

그러나 두 함수 사이에는 몇 가지 차이점이 있습니다. 데이터가 복사가 되는지 여부와, 데이터 유형이 변화되는지 여부의 차이가 있습니다.

 

 

차이점 비교


  • 복사 여부
    - np.array(): 기본적으로 배열의 복사본을 생성합니다.
    - np.asarray(): 배열의 복사본을 생성하지 않고, 가능한 경우에는 입력 배열의 뷰(view)를 반환합니다. 즉, 입력 배열과 반환된 배열이 메모리를 공유할 수 있습니다.

 

  • 데이터 유형 변환:

    - np.array(): 입력된 데이터의 유형에 따라 새로운 배열의 데이터 유형이 결정됩니다. 필요에 따라 데이터 유형을 명시적으로 지정할 수도 있습니다.
    - np.asarray(): 입력된 배열과 동일한 데이터 유형을 갖는 배열을 생성합니다. 따라서 입력 배열의 데이터 유형을 유지합니다.

 

코드 비교


간단한 예제로 두 함수의 동작을 살펴보겠습니다. 아래의 예제에서 np.array()를 사용하여 배열 arr1을 생성하고, 이를 np.asarray()로 배열 arr2를 생성합니다. 그런 다음 arr1의 첫 번째 요소를 변경하면 arr2도 동일하게 변경됩니다. 즉, np.asarray()는 입력 배열과 같은 데이터를 참조하므로 변경 사항이 반영됩니다. 즉, arr1의 0번 인덱스만 변경했음에도, np.asarray은 array을 데이터를 그대로 참조하며, 뷰를 생성하므로 arr1을 변경함에따라 arr2도 변경됩니다.

요약하자면, np.array()는 항상 복사본을 생성하며, 데이터 유형 변환을 수행할 수 있습니다. np.asarray()는 가능한 경우에는 복사본 대신 입력 배열의 뷰를 반환하며, 데이터 유형 변환은 수행하지 않고 입력 배열의 데이터 유형을 유지합니다.

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.asarray(arr1)

arr1[0] = 99

print(arr1)  # 출력: [99  2  3]
print(arr2)  # 출력: [99  2  3]

 

반응형

input = "abccbaz"


# 필랜드롬인지 판정하는 함수
def is_palindrome(sub_str:str) -> str:
    return sub_str == sub_str[::-1]

# 가장 긴 필랜드롬
def search_longest_palindrome(string:str) -> str:

    # 문자열의 길이가 2이하인데, 0번, 1번 문자열이 같은 경우
    if len(string) <= 2:
        if is_palindrome(string):
            return string

    longest_palindrome = str()

    for start_idx in range(1, len(string)):
        for window in range(1, start_idx):
            substring = string[start_idx-window:start_idx+window]
            if is_palindrome(substring):
                if len(longest_palindrome) < len(substring):
                    longest_palindrome = substring


    return longest_palindrome

search_longest_palindrome(input)

 

 

애너그램(Anagram)이란?

 


애너그램은 문자를 재배열하여, 다른 뜻을 가지는 단어로 바꾸는 것을 말한다. 예를 들어, 리그 오브 레전드의 챔피언과 "마오카이(Maokai)"은 "I am oak(떡갈나무)"와 같이 단어의 수와 각각의 문자열이 동일한데, 순서만 재배치함으로써, 다른 뜻을 가지는 단어들을 의미한다. 파이썬에서 단어들을 이런 애너그램 단위로 묶으려면 어떻게 해야할까? 다음과 같은 문제가 주어졌다고 가정하자.

 

아래와 같이 문자열이 주어졌을 때, 애너그램단위로 문자열 6개를 각각 유니크한 애너그램 단위로 묶고자한다.

inputs = ["education", "auctioned", "cautioned", "eat", "ate", "bet"]

 

솔루션

defaultdict을 이용하면, 바로 Key의 생성없이 Key, value의 할당이 가능하다. 각각의 인자를 list에 바로 저장할 때, 문자열이 애나그램인지 파악하기 위해서, 애너그램여부를 sorted()을 이용해서 정렬해서 파악한다. sorted()은 리스트, 문자열을 인자로 받을 수 있어서, 이를 이용해서 "".join(sorted(word))의 구문을 실행시키면, 정렬된 문자열이 반환된다.

# python3.x

inputs = ["education", "auctioned", "cautioned", "eat", "ate", "bet"]

# Solution
from collections import defaultdict

def sorted_words(inputs):
    anagram_groups = defaultdict(list)
    for idx, word in enumerate(inputs):
        anagram_groups["".join(sorted(word))].append(idx)

    return anagram_groups

 

 

팰린드롬(Palindrome, 회문)


팰린드롬(Palindrome)은 흔히 "요기요"는 거꾸로 해도 "요기요", "소주 만 병 만 주소"와 같이, 거꾸로 해도 똑같은 의미의 문장이 되는 문자열을 의미한다. 팰린드롬은 주어진 문자열이 있을 때, 문자열내에 팰린드롬이 있을 수도 있고, 없을 수도 있다. 예를 들어, "소주 주세요"와 같은 문자열은 팰린드롬이 없지만, "이 글의 원어는 국립국어원의 출처입니다"과 같이 팰린드롬이 모든 문자열이 아닌, 전체 문자열의 부분으로 가지고 있는 경우도 있다. 이렇듯, 문자열이 주어졌을 때, 가장 긴 팰린드롬을 찾아보자.

 

input = "abccbaz"

 

솔루션: 1) 우선 필랜드롬인지 검사하는 함수가 하나 독립적으로 있으면, 디자인이 쉬워진다. 그 후, 포인터 2개(첫 번째는 시작포인터, 두 번째는 윈도우)를 이용해서 검사를 진행한다.

 

아래와 같이 문자열이 주어졌을 때, 필랜드롬은 적어도 문자열의 길이가 2인 경우만 취급한다. 따라서, 필랜드롬의 문자가 2개이하면서, 0번, 1번의 문자가 동일한 경우는 조기 return을 시켜줄 수 있다.

# 가장 긴 필랜드롬
def search_longest_palindrome(string:str) -> str:

    # 문자열의 길이가 2이하인데, 0번, 1번 문자열이 같은 경우
    if len(string) <= 2:
        if is_palindrome(string):
            return string

 

그 외의 대부분의 경우를 위한 함수를 작성할 때는 아래와 같이 로직을 짤 수 있다. 1) 필랜드롬을 검사하기위한 시작인덱스(start_idx)는 0이 아니라 1부터 시작한다. 2) 윈도우사이즈는 start_idx을 넘으면 indexError가 날 것이기에, start_idx을 넘으면안된다. 또한 start_idx은 1부터 시작한다(0이면 시작점의 알파벳 하나를 비교하는 꼴이기 때문). 그리고, 가장 긴 필랜드롬을 찾는 것이기 때문에, 중간에 저장해놓을 변수를 하나 지정해서 다음과 같이 작성해볼 수 있다.

 

 

 

반응형

+ Recent posts