이동의 선택적 매개 변수?


464

Go가 선택적 매개 변수를 가질 수 있습니까? 아니면 같은 이름과 다른 수의 인수로 두 함수를 정의 할 수 있습니까?


관련 : 가변 매개 변수로 가변 변수를 사용할 때 필수 매개 변수를 적용하는 방법 은 다음과 같습니다. golang의 사용자 정의 라이브러리에서 컴파일 시간 오류를 트리거 할 수 있습니까?
icza

11
때로는 함수에 90 %의 유스 케이스가 있고 10 %의 유스 케이스가 있기 때문에 Google은 끔찍한 결정을 내 렸습니다. 선택적 인수는 해당 10 % 사용 사례입니다. Sane 기본값은 코드가 적고 코드가 적을수록 유지 관리 성이 향상됨을 의미합니다.
Jonathan

답변:


431

Go에는 선택적 매개 변수가 없으며 메서드 오버로드를 지원하지 않습니다 .

형식 일치를 수행 할 필요가없는 경우 메서드 디스패치가 단순화됩니다. 다른 언어를 사용한 경험에 따르면 이름은 같지만 서명이 다른 다양한 방법을 사용하는 것이 때로는 유용하지만 실제로는 혼란스럽고 깨지기 쉽습니다. 이름 만 일치하고 유형의 일관성을 요구하는 것은 Go의 유형 시스템에서 단순화 된 주요 결정이었습니다.


58
그렇다면 make특별한 경우입니까? 아니면 실제로 함수로 구현되지 않았
나요

65
@ Mk12 make는 언어 구성이며 위에서 언급 한 규칙은 적용되지 않습니다. 이 관련 질문을 참조하십시오 .
nemo

7
rangemake그런 의미에서 와 같은 경우 입니다
thiagowfx

14
메소드 오버로드-이론적으로는 훌륭한 아이디어이며 잘 구현 될 때 우수합니다. 그러나 나는 실제로 쓰레기 해독 오버로드를 목격 따라서 구글의 결정에 동의 할 것
trevorgk

118
나는 사지로 나가서이 선택에 동의하지 않을 것입니다. 언어 설계자들은 기본적으로 "우리가 원하는 언어를 디자인하려면 함수 오버로딩이 필요하다. 따라서 제작, 범위 등은 본질적으로 과부하가 걸리지 만, 원하는 API를 디자인하기 위해 함수 오버로딩을 원한다면 어렵다"고 말했다. 일부 프로그래머가 언어 기능을 오용한다는 사실은 기능을 제거하기위한 주장이 아닙니다.
Tom

216

선택적 매개 변수와 같은 것을 달성하는 좋은 방법은 가변형 인수를 사용하는 것입니다. 이 함수는 실제로 지정한 유형의 슬라이스를받습니다.

func foo(params ...int) {
    fmt.Println(len(params))
}

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}

"함수는 실제로 지정한 유형의 슬라이스를받습니다"어떻게 그렇게합니까?
Alix Axel

3
위의 예에서, params한 조각의 정수입니다
Ferguzz

76
그러나 같은 유형의 매개 변수에 대해서만 :(
Juan de Parras

15
@JuandeParras 글쎄요, ... interface {} 같은 것을 사용할 수 있습니다.
maufl

5
... type을 사용하면 개별 옵션의 의미를 전달하지 않습니다. 대신 구조체를 사용하십시오. ... type은 호출 전에 배열에 넣어야하는 값에 유용합니다.
user3523091

170

매개 변수가 포함 된 구조체를 사용할 수 있습니다.

type Params struct {
  a, b, c int
}

func doIt(p Params) int {
  return p.a + p.b + p.c 
}

// you can call it without specifying all parameters
doIt(Params{a: 1, c: 9})

12
구조체가 기본값을 가질 수 있다면 좋을 것입니다. 사용자가 생략 한 것은 기본적으로 해당 유형의 nil 값으로 설정되는데, 이는 함수에 대한 적절한 기본 인수 일 수도 있고 아닐 수도 있습니다.
jsdw

41
@lytnus, 나는 머리카락을 나누는 것을 싫어하지만 값이 생략 된 필드는 기본값으로 '제로 값'으로 설정됩니다. nil은 다른 동물입니다. 생략 된 필드의 유형이 포인터 인 경우 0 값은 nil입니다.
burfl

2
@burfl 예, "0 값"이라는 개념을 제외하고는 int / float / string 타입에 절대적으로 쓸모가 없습니다. 왜냐하면 그 값은 의미가 있고 값이 구조체에서 생략되었는지 또는 0 값인지에 대한 차이를 알 수 없기 때문입니다. 의도적으로 통과했습니다.
keymone

3
@ keymone, 나는 당신과 동의하지 않습니다. 사용자가 생략 한 값의 기본값이 "해당 유형에 대한 nil 값"으로 잘못되었다는 위의 진술에 대해 꼼짝 못하고있었습니다. 유형이 포인터인지 여부에 따라 0이 될 수도 있고 없을 수도 있습니다.
burfl

124

임의의 잠재적으로 많은 수의 선택적 매개 변수의 경우 기능 옵션 을 사용하는 것이 좋습니다 .

type의 Foobar경우 먼저 생성자를 하나만 작성 하십시오 .

func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
  fb := &Foobar{}
  // ... (write initializations with default values)...
  for _, op := range options{
    err := op(fb)
    if err != nil {
      return nil, err
    }
  }
  return fb, nil
}

여기서 각 옵션은 Foobar를 변경하는 기능입니다. 그런 다음 사용자가 다음과 같은 표준 옵션을 사용하거나 만들 수있는 편리한 방법을 제공하십시오.

func OptionReadonlyFlag(fb *Foobar) error {
  fb.mutable = false
  return nil
}

func OptionTemperature(t Celsius) func(*Foobar) error {
  return func(fb *Foobar) error {
    fb.temperature = t
    return nil
  }
}

운동장

간결성을 위해 옵션 유형 ( 놀이터 )에 이름을 지정할 수 있습니다 .

type OptionFoobar func(*Foobar) error

필수 매개 변수가 필요한 경우 variadic 앞에 생성자의 첫 번째 인수로 추가하십시오 options.

기능 옵션 관용구 의 주요 이점은 다음 과 같습니다.

  • 새로운 옵션이 필요할 때 컨스트럭터 서명이 동일하게 유지되므로 기존 코드를 손상시키지 않고 시간이 지남에 따라 API가 커질 수 있습니다.
  • 기본 유스 케이스가 가장 단순합니다. 인수가 전혀 없습니다!
  • 복잡한 값의 초기화를 세밀하게 제어 할 수 있습니다.

이 기술은 Rob Pike 가 만들었 으며 Dave Cheney 도 시연했습니다 .



15
영리하지만 너무 복잡합니다. Go의 철학은 간단한 방법으로 코드를 작성하는 것입니다. 구조체를 전달하고 기본값을 테스트하십시오.
user3523091

9
이 관용구의 원저자 인 최소한 첫 번째 출판인 인 FYI는 Go 철학에 대한 권위를 가진 Rob Pike 사령관입니다. 링크 - commandcenter.blogspot.bg/2014/01/... . 또한 "단순은 복잡하다"를 검색하십시오.
Petar Donchev

2
#JMTCW,하지만이 접근법은 추론하기가 매우 어렵다는 것을 알았습니다. 필자는 func()이 접근 방식으로 내 두뇌를 구부리는 것보다 필요한 경우 속성을 가질 수있는 값의 구조체를 전달하는 것을 훨씬 선호합니다 . 에코 라이브러리와 같이이 접근 방식을 사용해야 할 때마다 뇌가 추상화의 토끼 구멍에 갇히게됩니다. #fwiw
MikeSchinkel

고마워,이 관용구를 찾고있었습니다. 수락 된 답변만큼 거기에있을 수 있습니다.
r --------- k


6

아니요. 당 C ++ 프로그래머를위한 이동 문서,

Go는 함수 오버로딩을 지원하지 않으며 사용자 정의 연산자를 지원하지 않습니다.

선택적 매개 변수가 지원되지 않는다는 똑같이 명확한 진술을 찾을 수 없지만 지원되지 않습니다.


8
"이 [선택적 매개 변수]에 대한 현재 계획이 없습니다." Ian Lance Taylor, Go 어학 팀. groups.google.com/group/golang-nuts/msg/030e63e7e681fd3e
peterSO

사용자 정의 연산자는 3D 그래픽에서 종종 사용되는 선형 또는 대수학에 대한 도트 제품 또는 크로스 제품과 같은 매끄러운 수학 라이브러리의 핵심이기 때문에 사용자 정의 연산자는 끔찍한 결정입니다.
Jonathan

4

아래 내용과 비슷한 기능으로 이것을 꽤 잘 캡슐화 할 수 있습니다.

package main

import (
        "bufio"
        "fmt"
        "os"
)

func main() {
        fmt.Println(prompt())
}

func prompt(params ...string) string {
        prompt := ": "
        if len(params) > 0 {
                prompt = params[0]
        }
        reader := bufio.NewReader(os.Stdin)
        fmt.Print(prompt)
        text, _ := reader.ReadString('\n')
        return text
}

이 예에서 기본적으로 프롬프트에는 콜론과 그 앞에 공백이 있습니다. . .

: 

. . . 그러나 프롬프트 기능에 매개 변수를 제공하여이를 대체 할 수 있습니다.

prompt("Input here -> ")

아래와 같은 프롬프트가 나타납니다.

Input here ->

3

나는 params와 variadic args의 구조를 조합하여 사용했습니다. 이 방법으로 여러 서비스에서 소비 한 기존 인터페이스를 변경할 필요가 없었으며 서비스는 필요에 따라 추가 매개 변수를 전달할 수있었습니다. golang 놀이터의 샘플 코드 : https://play.golang.org/p/G668FA97Nu


3

Go 언어는 메서드 오버로딩을 지원하지 않지만 선택적 매개 변수와 같이 가변성 인수를 사용할 수 있으며 interface {}를 매개 변수로 사용할 수도 있지만 좋은 선택은 아닙니다.


2

나는 조금 늦었지만 유창한 인터페이스를 좋아한다면 체인 호출에 대한 세터를 다음과 같이 디자인 할 수 있습니다.

type myType struct {
  s string
  a, b int
}

func New(s string, err *error) *myType {
  if s == "" {
    *err = errors.New(
      "Mandatory argument `s` must not be empty!")
  }
  return &myType{s: s}
}

func (this *myType) setA (a int, err *error) *myType {
  if *err == nil {
    if a == 42 {
      *err = errors.New("42 is not the answer!")
    } else {
      this.a = a
    }
  }
  return this
}

func (this *myType) setB (b int, _ *error) *myType {
  this.b = b
  return this
}

그런 다음 다음과 같이 호출하십시오.

func main() {
  var err error = nil
  instance :=
    New("hello", &err).
    setA(1, &err).
    setB(2, &err)

  if err != nil {
    fmt.Println("Failed: ", err)
  } else {
    fmt.Println(instance)
  }
}

이것은 @Ripounet 답변에 제시된 기능 옵션 관용구 와 유사 하며 동일한 이점을 누리지 만 몇 가지 단점이 있습니다.

  1. 오류가 발생하면 즉시 중단되지 않으므로 생성자가 오류를 자주보고 할 경우 약간 덜 효율적입니다.
  2. err변수를 선언 하고 제로화 하는 선을 사용해야합니다.

그러나 가능한 작은 이점이 있습니다.이 유형의 함수 호출은 컴파일러가 인라인하기가 더 쉽지만 실제로는 전문가가 아닙니다.


이것은 빌더 패턴입니다
UmNyobe

2

맵과 함께 임의의 명명 된 매개 변수를 전달할 수 있습니다.

type varArgs map[string]interface{}

func myFunc(args varArgs) {

    arg1 := "default" // optional default value
    if val, ok := args["arg1"]; ok {
        // value override or other action
        arg1 = val.(string) // runtime panic if wrong type
    }

    arg2 := 123 // optional default value
    if val, ok := args["arg2"]; ok {
        // value override or other action
        arg2 = val.(int) // runtime panic if wrong type
    }

    fmt.Println(arg1, arg2)
}

func Test_test() {
    myFunc(varArgs{"arg1": "value", "arg2": 1234})
}

여기에이 접근 방식에 대한 몇 가지 논평이 있습니다 : reddit.com/r/golang/comments/546g4z/…
nobar


0

다른 가능성은 필드와 함께 유효한지 여부를 나타내는 구조체를 사용하는 것입니다. NullString 과 같은 SQL의 null 유형 이 편리합니다. 자체 유형을 정의 할 필요는 없지만 사용자 정의 데이터 유형이 필요한 경우 항상 동일한 패턴을 따를 수 있습니다. 옵션 정의는 함수 정의에서 명확하고 최소한의 추가 코드 또는 노력이 있다고 생각합니다.

예로서:

func Foo(bar string, baz sql.NullString){
  if !baz.Valid {
        baz.String = "defaultValue"
  }
  // the rest of the implementation
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.