답변:
다른 반환 형식에 의해 과부하, 무슨 말을 달리 입니다 가능하고 되고 일부 현대 언어에 의해 수행. 일반적인 반대는 코드에서
int func();
string func();
int main() { func(); }
어느 func()
것이 호출 되는지 알 수 없습니다 . 몇 가지 방법으로 해결할 수 있습니다.
int main() { (string)func(); }
.내가 정기적으로 ( ab ) 두 언어 는 반환 유형에 따라 과부하를 사용합니다 : Perl 및 Haskell . 그들이 무엇을하는지 설명하겠습니다.
Perl 에서는 스칼라 와 리스트 컨텍스트 사이에 근본적인 차이가 있습니다 (다른 것들도 있지만 두 가지가 있다고 가정합니다). Perl의 모든 내장 함수 는 호출되는 컨텍스트 에 따라 다른 작업을 수행 할 수 있습니다 . 예를 들어, join
연산자는 목록 컨텍스트 (결합되는 것)를 scalar
강제 하는 반면 연산자는 스칼라 컨텍스트를 강제합니다.
print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.
Perl의 모든 연산자는 스칼라 컨텍스트와 목록 컨텍스트에서 무언가를 수행하며 그림과 같이 다를 수 있습니다. (이것은 단지 임의의 연산자에 대한 것이 아닙니다 localtime
. @a
리스트 컨텍스트에서 배열을 사용하면 스칼라 컨텍스트에서 배열을 반환하지만 요소 수를 반환하므로 예를 들어 print @a
요소를 print 0+@a
인쇄하고 크기 를 인쇄합니다. ) 또한 모든 연산자는 컨텍스트를 강제 할 수 있습니다 ( 예 : 추가 +
는 스칼라 컨텍스트를 강제합니다). man perlfunc
이 문서의 모든 항목에는 이 내용이 기록되어 있습니다. 예를 들어, 여기에 대한 항목 중 일부가 있습니다 glob EXPR
.
목록 컨텍스트
EXPR
에서 표준 Unix 셸과 같은 값에 대한 파일 이름 확장명 목록 (비어있을 수 있음)을 반환합니다/bin/csh
. 스칼라 컨텍스트에서 glob은 이러한 파일 이름 확장을 반복하여 목록이 소진되면 undef를 반환합니다.
이제리스트와 스칼라 컨텍스트의 관계는 무엇입니까? 음, man perlfunc
말한다
다음과 같은 중요한 규칙을 기억하십시오. 목록 컨텍스트의 표현식 동작과 스칼라 컨텍스트의 동작을 연관시키는 규칙은 없으며 그 반대도 마찬가지입니다. 완전히 다른 두 가지 일을 할 수 있습니다. 각 연산자와 함수는 스칼라 컨텍스트에서 반환하는 것이 가장 적합한 값을 결정합니다. 일부 연산자는 목록 컨텍스트에서 반환 된 목록의 길이를 반환합니다. 일부 연산자는 목록에서 첫 번째 값을 반환합니다. 일부 연산자는 목록에서 마지막 값을 반환합니다. 일부 연산자는 성공적인 작업 수를 반환합니다. 일관성을 원치 않는 한 일반적으로 원하는 것을 수행합니다.
따라서 단일 기능을 갖는 것은 간단한 일이 아니며 마지막에 간단한 변환을 수행합니다. 사실, 나는 localtime
그런 이유로 그 예를 선택했습니다 .
이 동작을하는 것은 내장 기능 만이 아닙니다. 모든 사용자는을 사용하여 이러한 함수를 정의 할 수 있습니다 wantarray
.이를 통해 list, 스칼라 및 void 컨텍스트를 구별 할 수 있습니다. 예를 들어, 빈 공간에서 호출되는 경우 아무것도하지 않기로 결정할 수 있습니다.
지금, 당신은이 아니라고 불평 할 수있다 진실 만이 호출 한 다음 해당 정보에 작용 년대 상황을 이야기 하나 개 기능을 가지고 있기 때문에 반환 값에 의해 오버로드가. 그러나 이것은 분명히 동등합니다 (그리고 Perl이 문자 그대로 일반적인 과부하를 허용하지 않지만 함수가 인수를 검사하는 방법과 유사합니다). 또한이 응답의 시작 부분에 언급 된 모호한 상황을 훌륭하게 해결합니다. Perl은 어떤 메소드를 호출해야할지 모른다고 불평하지 않습니다. 그냥 호출합니다. 함수가 호출 된 컨텍스트를 파악하기 만하면됩니다. 항상 가능합니다.
sub func {
if( not defined wantarray ) {
print "void\n";
} elsif( wantarray ) {
print "list\n";
} else {
print "scalar\n";
}
}
func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"
(참고 : 때때로 기능을 의미 할 때 Perl 연산자라고 말할 수도 있습니다.이 논의에서는 중요하지 않습니다.)
Haskell 은 다른 접근 방식, 즉 부작용이 없습니다. 또한 강력한 유형 시스템을 가지고 있으므로 다음과 같은 코드를 작성할 수 있습니다.
main = do n <- readLn
print (sqrt n) -- note that this is aligned below the n, if you care to run this
이 코드는 표준 입력에서 부동 소수점 숫자를 읽고 제곱근을 인쇄합니다. 그러나 이것에 대한 놀라운 점은 무엇입니까? 음의 유형은 readLn
입니다 readLn :: Read a => IO a
. 이것이 의미하는 것은 Read
( Read
형식 클래스 의 인스턴스 인 모든 형식) 모든 형식에 대해 readLn
읽을 수 있다는 것입니다. Haskell은 부동 소수점 숫자를 읽고 싶다는 것을 어떻게 알았습니까? 음, 유형 sqrt
IS sqrt :: Floating a => a -> a
, 본질적 수단 sqrt
만을 입력으로 부동 소수점 숫자에 동의 한 하스켈 내가 원하는 것을 추론 그렇게 할 수 있습니다.
Haskell이 내가 원하는 것을 추론 할 수없는 경우 어떻게됩니까? 글쎄, 몇 가지 가능성이 있습니다. 반환 값을 전혀 사용하지 않으면 Haskell은 단순히 처음에는 함수를 호출하지 않습니다. 나는 그러나, 할 반환 값을 사용하여 다음 하스켈은 유형을 추론 할 수 없다는 불평 할 것이다 :
main = do n <- readLn
print n
-- this program results in a compile-time error "Unresolved top-level overloading"
원하는 유형을 지정하여 모호성을 해결할 수 있습니다.
main = do n <- readLn
print (n::Int)
-- this compiles (and does what I want)
어쨌든,이 전체 토론의 의미는 반환 값으로 과부하가 가능하고 완료되어 귀하의 질문의 일부에 답변한다는 것입니다.
질문의 다른 부분은 왜 더 많은 언어가 그렇게하지 않는가입니다. 다른 사람들이 그 대답을하도록하겠습니다. 그러나 몇 가지 의견은 다음과 같습니다. 주된 이유는 아마도 혼동의 기회가 인수 유형에 의한 과부하보다 실제로 더 클 수 있다는 것입니다. 개별 언어의 근거를 볼 수도 있습니다.
Ada : "가장 간단한 과부하 해결 규칙은 가능한 한 넓은 컨텍스트의 모든 정보를 사용하여 오버로드 된 참조를 해결하는 것입니다.이 규칙은 간단하지만 도움이되지 않습니다. 인간 독자가 필요합니다. 임의로 큰 텍스트 조각을 스캔하고 임의로 복잡한 추론을 만드는 것 (예 : 위의 (g)) 우리는 더 나은 규칙이 인간 독자 나 컴파일러가 수행해야하는 작업을 명시 적으로 만드는 규칙이라고 믿습니다. 인간 독자에게는 가능한 한 자연 스럽습니다. "
C ++ (Bjarne Stroustrup의 "C ++ 프로그래밍 언어"섹션 7.4.1) : "리턴 유형은 과부하 해석에서 고려되지 않습니다. 이유는 개별 연산자 또는 함수 호출의 컨텍스트에 독립적으로 해석을 유지하기위한 것입니다.
float sqrt(float);
double sqrt(double);
void f(double da, float fla)
{
float fl = sqrt(da); // call sqrt(double)
double d = sqrt(da); // call sqrt(double)
fl = sqrt(fla); // call sqrt(float)
d = sqrt(fla); // call sqrt(float)
}
리턴 유형이 고려되면 더 이상 sqrt()
개별 호출을보고 호출 된 함수를 판별 할 수 없습니다. "(비교를 위해 Haskell에서는 암시 적 변환 이 없음을 참고하십시오 .)
Java ( Java Language Specification 9.4.1 ) : "상속 된 메소드 중 하나는 다른 모든 상속 된 메소드에 대해 리턴 유형으로 대체 가능해야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다." (예, 이것이 이론적 근거를 제시하지 않는다는 것을 알고 있습니다. 저는 Gosling이 "Java Programming Language"로 이론적 근거를 제공 할 것이라고 확신합니다. 누군가가 사본을 가지고 있을까요? 그러나 Java에 대한 재미있는 사실 : JVM 은 반환 값으로 과부하를 허용 합니다! 예를 들어 Scala 에서 사용 되며 내부를 가지고 놀 면서 Java 를 통해 직접 액세스 할 수 있습니다 .
추신. 마지막으로 C ++에서 반환 값을 사용하여 과부하로 실제로 과부하 할 수 있습니다. 증거:
struct func {
operator string() { return "1";}
operator int() { return 2; }
};
int main( ) {
int x = func(); // calls int version
string y = func(); // calls string version
double d = func(); // calls int version
cout << func() << endl; // calls int version
func(); // calls neither
}
Foo
하고 Bar
양방향 변환을 지원 한다고 가정 하면 메서드는 Foo
내부적으로 유형을 사용 하지만 유형을 반환합니다 Bar
. 이러한 메소드가 코드에 의해 호출되어 결과를 type으로 즉시 강제 변환 Foo
하면 Bar
리턴 유형을 사용하는 Foo
것이 좋지만 더 나은 방법입니다. BTW, 나는 또한 어떤 수단을보고 싶습니다 ...
var someVar = someMethod();
있습니다. 예를 들어, 유창함 인터페이스를 변경할 수 불변의 버전을 혜택을 얻을 수있는 구현하는 유형의 가족은, 그래서 var thing2 = thing1.WithX(3).WithY(5).WithZ(9);
것 WithX(3)
사본 thing1
변경 가능한 객체의 mutate X 및 변경 가능한 객체를 돌려로를; WithY(5)
Y를 변경하고 동일한 객체를 반환합니다. 마찬가지로`WithZ (9). 그러면 할당이 불변 유형으로 변환됩니다.
함수가 리턴 유형에 의해 오버로드되고이 두 개의 과부하가 발생한 경우
int func();
string func();
컴파일러가 이와 같은 호출을 볼 때 호출 할 두 함수 중 어떤 것을 알아낼 수있는 방법은 없습니다
void main()
{
func();
}
이러한 이유로 언어 디자이너는 종종 반환 값 오버로드를 허용하지 않습니다.
일부 언어 (예 : MSIL 등), 그러나 않는 반환 형식에 의해 오버로드 할 수 있습니다. 물론 위의 어려움에 직면했지만 해결 방법이 있으므로 설명서를 참조해야합니다.
그러한 언어로 다음을 어떻게 해결 하시겠습니까?
f(g(x))
경우 f
과부하를했다 void f(int)
및 void f(string)
및 g
과부하를 가지고 int g(int)
와 string g(int)
? 어떤 종류의 명확성이 필요합니다.
나는 당신이 이것을 필요로하는 상황이 기능의 새로운 이름을 선택함으로써 더 잘 제공 될 것이라고 생각합니다.
다른 매우 유사한 질문 (dupe?) 에서 C ++ 특정 답변 을 훔치려면 :
Stroustrup (다른 C ++ 건축가의 입력으로 가정)이 과부하 해상도를 '컨텍스트 독립적'으로 원했기 때문에 단순히 함수 반환 유형이 과부하 해상도에서 작동하지 않습니다. "C ++ 프로그래밍 언어, 제 3 판"의 7.4.1- "오버로드 및 리턴 유형"을 참조하십시오.
그 이유는 개별 연산자 또는 함수 호출의 컨텍스트 독립적 인 해결을 유지하기 위함입니다.
그들은 결과가 어떻게 사용되었는지가 아니라 과부하가 어떻게 호출되었는지에 기반을두기를 원했습니다. 실제로 많은 함수가 결과를 사용하지 않고 호출되거나 결과가 더 큰 표현식의 일부로 사용됩니다. 그들이 결정했을 때 내가 확신하게 된 한 가지 요소는 반환 유형이 해상도의 일부 인 경우 복잡한 규칙으로 해결되거나 컴파일러 던지기가 필요한 오버로드 된 함수에 대한 많은 호출이 있다는 것입니다 호출이 모호하다는 오류.
그리고 주님은 C ++ 과부하 해상도가 충분히 복잡하다는 것을 알고 있습니다.
haskell에서는 기능 과부하가 없어도 가능합니다. 하스켈은 타입 클래스를 사용합니다. 프로그램에서 당신은 볼 수 있습니다 :
class Example a where
example :: Integer -> a
instance Example Integer where -- example is now implemented for Integer
example :: Integer -> Integer
example i = i * 10
함수 과부하 자체는 그렇게 인기가 없습니다. 내가 본 대부분의 언어는 C ++, 아마도 java 및 / 또는 C #입니다. 모든 동적 언어에서는 다음과 같은 축약 형입니다.
define example:i
↑i type route:
Integer = [↑i & 0xff]
String = [↑i upper]
def example(i):
if isinstance(i, int):
return i & 0xff
elif isinstance(i, str):
return i.upper()
따라서 아무 의미가 없습니다. 대부분의 사람들은 언어가 당신이 사용하는 곳마다 한 줄씩 떨어 뜨릴 수 있는지에 관심이 없습니다.
패턴 매칭은 함수 오버로딩과 다소 비슷하며 때로는 비슷하게 작동한다고 생각합니다. 비록 소수의 프로그램에만 유용하고 대부분의 언어에서 구현하기 까다로워서 일반적이지 않습니다.
당신은 언어를 구현하기 위해 구현하기 쉬운 다른 많은 기능들이 무한히 많이 있음을 알 수 있습니다.
좋은 답변입니다! A. Rex의 답변은 특히 매우 상세하고 유익합니다. 그가 지적했듯이 C ++ 은 컴파일 할 때 사용자 제공 유형 변환 연산자를 고려합니다 lhs = func();
(여기서 func는 실제로 구조체의 이름입니다) . 내 해결 방법은 약간 다릅니다-더 나은 것은 아니며 단지 다릅니다 (같은 기본 아이디어를 기반으로하지만).
내가 쓰고 싶었던 반면에 ...
template <typename T> inline T func() { abort(); return T(); }
template <> inline int func()
{ <<special code for int>> }
template <> inline double func()
{ <<special code for double>> }
.. etc, then ..
int x = func(); // ambiguous!
int x = func<int>(); // *also* ambiguous!? you're just being difficult, g++!
나는 매개 변수가있는 구조체를 사용하는 솔루션으로 끝났습니다 (T = 반환 유형).
template <typename T>
struct func
{
operator T()
{ abort(); return T(); }
};
// explicit specializations for supported types
// (any code that includes this header can add more!)
template <> inline
func<int>::operator int()
{ <<special code for int>> }
template <> inline
func<double>::operator double()
{ <<special code for double>> }
.. etc, then ..
int x = func<int>(); // this is OK!
double d = func<double>(); // also OK :)
이 솔루션의 이점은 이러한 템플릿 정의를 포함하는 모든 코드가 더 많은 유형에 대한 전문화를 추가 할 수 있다는 것입니다. 또한 필요에 따라 구조체의 부분 특수화를 수행 할 수 있습니다. 예를 들어 포인터 유형에 대한 특수 처리를 원하는 경우 :
template <typename T>
struct func<T*>
{
operator T*()
{ <<special handling for T*>> }
};
부정적으로, 당신은 int x = func();
내 솔루션으로 쓸 수 없습니다 . 당신은 작성해야합니다 int x = func<int>();
. 컴파일러에게 형식 변환 연산자를보고이를 요청하도록 요청하는 대신 반환 형식이 무엇인지 명시 적으로 말해야합니다. "내"솔루션과 A.Rex는 모두이 C ++ 딜레마를 해결하기위한 파레토 최적화 방식에 속한다고 말합니다. :)
반환 유형이 다른 메소드를 오버로드 하려면 오버로드 실행을 허용하도록 더미 매개 변수를 기본값으로 추가하십시오 .
type
myclass = class
public
function Funct1(dummy: string = EmptyStr): String; overload;
function Funct1(dummy: Integer = -1): Integer; overload;
end;
이렇게 사용하세요
procedure tester;
var yourobject : myclass;
iValue: integer;
sValue: string;
begin
yourobject:= myclass.create;
iValue:= yourobject.Funct1(); //this will call the func with integer result
sValue:= yourobject.Funct1(); //this will call the func with string result
end;
이미 표시된 것처럼 반환 유형에 따라 다른 함수의 모호한 호출은 모호성을 유발합니다. 모호성은 결함있는 코드를 유발합니다. 결함이있는 코드는 피해야합니다.
모호한 시도로 인한 복잡성은 이것이 좋은 해킹이 아님을 보여줍니다. 지적 운동 외에도 참조 매개 변수가있는 절차를 사용하지 않는 것이 좋습니다.
procedure(reference string){};
procedure(reference int){};
string blah;
procedure(blah)
doing(thisVery(deeplyNested(), andOften(butNotAlways()), notReally()), goodCode());
.NET에서는 때로는 하나의 매개 변수를 사용하여 일반 결과에서 원하는 출력을 표시 한 다음 예상 한 결과를 얻기 위해 변환했습니다.
public enum FooReturnType{
IntType,
StringType,
WeaType
}
class Wea {
public override string ToString()
{
return "Wea class";
}
}
public static object Foo(FooReturnType type){
object result = null;
if (type == FooReturnType.IntType)
{
/*Int related actions*/
result = 1;
}
else if (type == FooReturnType.StringType)
{
/*String related actions*/
result = "Some important text";
}
else if (type == FooReturnType.WeaType)
{
/*Wea related actions*/
result = new Wea();
}
return result;
}
static void Main(string[] args)
{
Console.WriteLine("Expecting Int from Foo: " + Foo(FooReturnType.IntType));
Console.WriteLine("Expecting String from Foo: " + Foo(FooReturnType.StringType));
Console.WriteLine("Expecting Wea from Foo: " + Foo(FooReturnType.WeaType));
Console.Read();
}
이 예제가 도움이 될 수도 있습니다.
#include <iostream>
enum class FooReturnType{ //Only C++11
IntType,
StringType,
WeaType
}_FooReturnType;
class Wea{
public:
const char* ToString(){
return "Wea class";
}
};
void* Foo(FooReturnType type){
void* result = 0;
if (type == FooReturnType::IntType) //Only C++11
{
/*Int related actions*/
result = (void*)1;
}
else if (type == FooReturnType::StringType) //Only C++11
{
/*String related actions*/
result = (void*)"Some important text";
}
else if (type == FooReturnType::WeaType) //Only C++11
{
/*Wea related actions*/
result = (void*)new Wea();
}
return result;
}
int main(int argc, char* argv[])
{
int intReturn = (int)Foo(FooReturnType::IntType);
const char* stringReturn = (const char*)Foo(FooReturnType::StringType);
Wea *someWea = static_cast<Wea*>(Foo(FooReturnType::WeaType));
std::cout << "Expecting Int from Foo: " << intReturn << std::endl;
std::cout << "Expecting String from Foo: " << stringReturn << std::endl;
std::cout << "Expecting Wea from Foo: " << someWea->ToString() << std::endl;
delete someWea; // Don't leak oil!
return 0;
}
이것은 C ++과 약간 다릅니다. 반환 유형으로 직접 과부하로 간주되는지 알 수 없습니다. 그것은 방식으로 작동하는 템플릿 전문화에 가깝습니다.
util.h
#ifndef UTIL_H
#define UTIL_H
#include <string>
#include <sstream>
#include <algorithm>
class util {
public:
static int convertToInt( const std::string& str );
static unsigned convertToUnsigned( const std::string& str );
static float convertToFloat( const std::string& str );
static double convertToDouble( const std::string& str );
private:
util();
util( const util& c );
util& operator=( const util& c );
template<typename T>
static bool stringToValue( const std::string& str, T* pVal, unsigned numValues );
template<typename T>
static T getValue( const std::string& str, std::size_t& remainder );
};
#include "util.inl"
#endif UTIL_H
util.inl
template<typename T>
static bool util::stringToValue( const std::string& str, T* pValue, unsigned numValues ) {
int numCommas = std::count(str.begin(), str.end(), ',');
if (numCommas != numValues - 1) {
return false;
}
std::size_t remainder;
pValue[0] = getValue<T>(str, remainder);
if (numValues == 1) {
if (str.size() != remainder) {
return false;
}
}
else {
std::size_t offset = remainder;
if (str.at(offset) != ',') {
return false;
}
unsigned lastIdx = numValues - 1;
for (unsigned u = 1; u < numValues; ++u) {
pValue[u] = getValue<T>(str.substr(++offset), remainder);
offset += remainder;
if ((u < lastIdx && str.at(offset) != ',') ||
(u == lastIdx && offset != str.size()))
{
return false;
}
}
}
return true;
}
util.cpp
#include "util.h"
template<>
int util::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoi( str, &remainder );
}
template<>
unsigned util::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoul( str, &remainder );
}
template<>
float util::getValue( const std::string& str, std::size_t& remainder ) {
return std::stof( str, &remainder );
}
template<>
double util::getValue( const std::string& str, std::size_t& remainder ) {
return std::stod( str, &remainder );
}
int util::convertToInt( const std::string& str ) {
int i = 0;
if ( !stringToValue( str, &i, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
throw strStream.str();
}
return i;
}
unsigned util::convertToUnsigned( const std::string& str ) {
unsigned u = 0;
if ( !stringToValue( str, &u, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
throw strStream.str();
}
return u;
}
float util::convertToFloat(const std::string& str) {
float f = 0;
if (!stringToValue(str, &f, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
throw strStream.str();
}
return f;
}
double util::convertToDouble(const std::string& str) {
float d = 0;
if (!stringToValue(str, &d, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to double";
throw strStream.str();
}
return d;
}
이 예제는 리턴 유형별로 함수 과부하 분석을 정확하게 사용하지 않지만이 C ++ 비 객체 클래스는 템플리트 특수화를 사용하여 전용 정적 메소드를 사용하여 리턴 유형별로 함수 과부하 분석을 시뮬레이션합니다.
각 convertToType
함수는 함수 템플리트를 호출하고 있으며이 함수 템플리트 stringToValue()
의 구현 세부 사항 또는 알고리즘을 보면 호출 getValue<T>( param, param )
하고 유형을 리턴 하여 매개 변수 중 하나로 함수 템플리트에 전달 된 유형 T
으로 저장합니다. .T*
stringToValue()
이와 같은 것 이외의 것; C ++에는 실제로 반환 유형별로 함수 오버로드 해상도를 갖는 메커니즘이 없습니다. 내가 모르는 다른 구문이나 메커니즘이 반환 유형별로 해상도를 시뮬레이션 할 수 있습니다.
나는 이것이 현대 C ++ 정의의 GAP이라고 생각합니다 ... 왜 그렇습니까?
int func();
double func();
// example 1. → defined
int i = func();
// example 2. → defined
double d = func();
// example 3. → NOT defined. error
void main()
{
func();
}
C ++ 컴파일러가 "3"예제에서 오류를 발생시키지 않고 "1 + 2"예제에서 코드를 수락 할 수없는 이유는 무엇입니까 ??
대부분의 정적 언어는 이제 제네릭을 지원하므로 문제를 해결할 수 있습니다. 앞에서 언급했듯이 매개 변수 diff가 없으면 어느 것을 호출할지 알 수있는 방법이 없습니다. 따라서이 작업을 수행하려면 제네릭을 사용하고 하루 만 호출하십시오.