몇 개의 논쟁이 통과 되었습니까?


33

선택한 언어를 사용하여 가변 개수의 인수를 사용하여 호출 한 인수 수를 반환하는 함수를 작성하십시오.

사양 :

  • 여러분의 언어는 가변적 인 인수 함수를 지원해야합니다.
  • 매개 변수는 개별적으로 전달 될 수 있어야합니다. 즉, 배열을 전달하면 하나의 매개 변수 만 계산됩니다. 언어가 지원하는 경우 "모든 전달 된 인수"배열을 사용할 수 있습니다. 함수 호출 방식에 제한이 있습니다.
  • 이 함수를 호출하는 코드가 소스 의 인수 수를 전달할 필요는 없습니다 . 컴파일러가 호출 규칙의 일부로 인수 수를 삽입하면 허용됩니다.
  • 인수는 원하는 모든 유형이 될 수 있습니다. 단일 유형 (예 : 지원 만 int유효), 임의 유형 (인수 유형이 허용됨) 또는 인수 유형의 조합 (예 : 첫 번째 arg is int, rest is strings) 만 지원할 수 있습니다 .
  • 함수는 최대 개수의 인수를 가질 수 있지만 (특히 리소스가 유한하기 때문에) 적어도 2 개의 인수를 지원해야합니다 .

시료:

  • f() 보고 0
  • f(1)또는 f("a")반환1
  • f([1, 2, 3])13 개의 인수가 아닌 배열이 전달 될 때 반환
  • f(1, 10)또는 f(1, "a")반환2

이것이 코드 골프이기 때문에 우승 솔루션은 가장 적은 수의 바이트를 사용하는 솔루션입니다.


4
"함수", "반환 값"또는 "가변 인수"가 무엇인지 (객관적으로) 명확하지는 않습니다. 예를 들어, Dodos 기능이 모나 딕 또는 바리 아딕으로 간주됩니까?
user202729

24
@ user202729 언어가 기능을 지원하지 않으면 다른 언어를 사용하십시오. 모든 언어가 경쟁 할 필요는 없으며, 코드 골프의 일환으로 업무에 적합한 도구를 찾는 것입니다.
Sanchises

5
@ user202729 나는 우리가 특이한 언어로만 가능한 가끔 도전을받는 것처럼 전통적인 / 고급 언어를 목표로하는 가끔 도전에 아무런 문제가 없습니다.
상추

6
언어 특성에 대한 명확한 문제를 해결하기 위해 중단 문제를 해결해야한다는 것을 몰랐습니다 ....
Conor O'Brien

5
언어에 인수 / 호출 규칙 개념이 없으면 임의의 수의 인수를 지원하는 기준에 맞지 않습니다.
Glenn Smith

답변:


15

BASIC의 Amstrad CPC Z80 바이너리 호출, 1 바이트, 16 진수 인코딩

C9          : RET

(또한 2 및 5 바이트 버전, 아래 참조)

호출에 들어가면 전달 된 매개 변수의 수는 A 레지스터에 있습니다. 코드는 단순히 즉시 반환합니다. Z80에는 반환 값에 대한 개념이 없으며 시작 및 종료 상태 만 있습니다. 코드는 PC(프로그램 카운터) 및 SP(스택 포인터)를 제외하고 입력 조건을 변경하지 않기 때문에 레지스터에서 액세스 할 수 있습니다 . 그러나의 값은 ABASIC에 액세스 할 수 없으며 거의 ​​즉시 덮어 씁니다.

예 :

CALL &8000, "Hello", "World"

A = 2

CALL &8000, 42

A = 1

CALL &8000

A = 0


요청에 따라 다음은 BASIC에서 값에 액세스 할 수있게하는 코드입니다. 5 바이트만으로도 가능하다는 사실에 놀랐습니다! :

기계 코드 :

12          : LD   (DE), A
13          : INC  DE
AF          : XOR  A
12          : LD   (DE), A
C9          : RET

입국시 :

  • AF -누산기 및 플래그 레지스터 (2 개의 8 비트 레지스터로 처리)
    • A 전달 된 매개 변수 수를 포함하여 최대 32 개의 매개 변수
    • 에 무엇이 있는지 잘 모르겠습니다 F. 0둘 다 정의되지 않은 두 플래그를 제외하고 는 모든 플래그가 재설정되는 것으로 보입니다 1. Z에 플래그 (영) 설정되어 1전달 된 매개 변수가 없다면
  • BC
    • B-32에서 매개 변수 개수를 뺀 값 ( A+ B= 32)
    • C - &FF
  • DE -마지막 매개 변수의 주소 또는 매개 변수가 전달되지 않은 경우 호출 주소
  • HL -현재 실행중인 토큰 화 된 BASIC 명령 이후의 첫 번째 바이트 주소 (프로그램 또는 즉시 명령 모드)
  • IX -마지막 매개 변수에 대한 포인터의 스택 주소
  • IY - &0000

코드

  1. Loa DDE값으로 지정된 주소A
  2. INCrements DE
  3. XORs A(포함 A)&00
  4. Loa Ds가 A가리키는 주소 값DE
  5. RET항아리

출구에서 :

  • A파괴된다 (항상이다 &00)
  • DE 파괴됨 (항상 엔트리보다 1이 높음)
  • 다른 모든 레지스터는 유지됩니다

기본

Amstrad basic에는 세 가지 데이터 유형과 간단한 배열이 있습니다. 기본적으로 모든 BASIC 변수는 REAL (서명, 32 비트 가수, 8 비트 지수)이며로 명시 적으로 지정할 수 있습니다 !. INTEGER (서명 된 16 비트) %및 STRING (1 바이트 문자열 길이, 최대 255 바이트 문자 데이터, 2 진 안전)의 경우 $다음을 사용하십시오 .

  • x -실제 (암시 적)
  • x! -실제 (명시 적)
  • x% - 정수
  • x$ -STRING

당신은 또한 사용할 수 있습니다 DEFINT, DEFREAL그리고 DEFSTR단일 문자, 또는 두 개의 단일 문자의 범위와 FORTRAN과 유사한 그 문자로 시작하는 모든 변수의 기본 유형을 지정할 수 있습니다.

  • DEFSTR a
  • DEFINT x-z

지금:

  • a -STRING (암시 적)
  • i -실제 (암시 적)
  • x -정수 (암시 적)
  • x$ -STRING (명시 적)

가장 쉬운 유형은 정수입니다. 머신 코드는 마지막 매개 변수가 값이 아닌 주소로 전달 될 것으로 예상하므로 변수 @앞에 접두사가 붙습니다. 반환 변수는 CALLs 매개 변수 중 하나로 계산됩니다 .

기계 코드는 BASIC에서 다음과 같이 호출됩니다 (주소에서 메모리에로드되었다고 가정 &8000).

CALL &8000, "Hello", "World", 42, @n%

n% = 4

초기 값에 관계없이 항상 올바른 결과를 제공합니다 n%.

모든 입력 레지스터를 유지하는 2 바이트 버전의 경우 :

CALL &8003, "Hello", "World", 42, @n%

n% = 4

처음 3 바이트는 건너 뛰고 초기 값 n%0-인 경우에만 올바른 결과를 제공합니다 255. Z80이 리틀 엔디안이기 때문에 작동합니다.

전달하기 전에 리턴 매개 변수를 초기화해야합니다. 그렇지 않으면 BASIC이 Improper argument오류를 발생시킵니다. 아래 이미지에서는 ?값 변경을 보여주기 위해 호출 직전과 직후에 반환 값을 인쇄하고 있습니다 ( 시연을 골프화 했으므로 바로 가기로 !). 부호있는 정수 &FFFF-1대한 이진 표현이므로 값을 사용하고 있습니다. 이것은 5 바이트 프로그램이 두 바이트를 모두 올바르게 쓰는 반면 2 바이트 프로그램은 낮은 바이트 만 쓰고 높은 바이트가 이미 있다고 가정합니다 &00.

enter image description here


그렇다면 사용하는 호출 규칙은 어떻게 값을 반환합니까? 그것은 축적에이를 반환하지 않습니다, 또는 모두에, 당신의 대답은 기본적으로 사용자 지정 호출 규칙을 발명하는 경우 그 반환 레지스터를 추가하는 대신에 당신이 저장할 수있는 포인터를 전달하여 해결할 수있는 문제 당신을위한 문제를 ( A그의 경우 실제로 BASIC에서 어떻게 할 수 있는지). 그에 문제가있는 것은 아니지만 기존의 캘링 규칙을 따르는 것이 더 흥미로운 답변 일 수 있습니다.
Peter Cordes

@PeterCordes Amstrad BASIC이나 Z80 모두 범위 개념이 없습니다. 모든 값은 전역 적이며 파괴 될 때까지 즉시 액세스 할 수 있습니다. 의 값은 명령 A직후에 동일합니다 RET. A누산기이므로 값의 수명 이 매우 짧습니다. 와 같은 것은 없습니다 x = CALL &8000, 42. CALL &8000, x, 42추가 Z80 코드 여야 하지만 그런 다음x2,하지 1.
CJ 데니스

카운트에 출력 인수를 포함하면 괜찮다고 생각합니다. 그렇지 않으면 1 바이트 감소 명령이 없습니까? 나는 실제로 사소하지 않고 BASIC에서 실제로 사용할 수있는 버전을보고 싶습니다.
Peter Cordes

1
@PeterCordes 완료! 아, 그건 그렇고, 매개 변수없이 호출하지 않는 것은 &00s로 자신의 처음 두 명령을 덮어 쓰므로 언급하지 않았습니다.NOP 노 작전. 안전을 위해 다른 바이트를 추가 할 수 있지만 물론 리턴 매개 변수가 없으면 아무것도 설정할 수 없습니다.
CJ 데니스

32

29
자바 구타 Javscript는 드문 enouth입니다
임의의 사람

3
@ Therandomguy 이것은 interface x{void f(Object...a);}정의 되어야하는 것과 같은 것을 필요 로하며,이 람다는 해당 인터페이스 유형의 변수에 저장되거나 해당 인터페이스 유형을 예상하는 메소드로 전달되어야 하므로이 도전에 대해 정말로 확실하지는 않습니다 (심지어도 일반적으로 자바 람다는 코드 골프 도전에 허용됩니다)
SamYonnou

3
@SamYonnou 다른 람다와는 차이가 없으며 언급했듯이 람다는 괜찮습니다 .
Olivier Grégoire

@ OlivierGrégoire 나는 람다가 허용된다는 것을 알고 있습니다. 예를 들어 JavaScript와 비교할 때 REPL과 같은 것을 사용하고 메인 클래스 / 메소드가 필요하지 않더라도 더 많은 코드가 필요합니다. 인터페이스를 정의해야 할 필요성은 JavaScript와 구별되는 것입니다)
SamYonnou

@ OlivierGrégoire : Java를 알고 있지만 전혀 유지하지 못했습니다. Java가 실제로 짧게 할 수있는 양탄자 밑에서 보일러 플레이트가 스윕되는 것에 대한 Sam의 의견을보고 싶었습니다. 나는 그것이 허용되어야한다는 것에 동의한다. 게다가, "Java beating JS"에 대한 답변으로 여전히 흥미 롭습니다.
Peter Cordes

25

자바 스크립트, 15 바이트

[].push.bind(0)

그만큼 Array.prototype.push 함수는 여러 개의 인수를 가져 와서 배열에 추가 한 다음 배열의 크기를 반환합니다. 따라서 push빈 배열에 사용 된 함수는에 제공된 인수 수를 반환합니다 push.

f = [].push.bind(0)

f(10,2,65,7)
> 4

f()
> 0

.bind(0)단순히 제공 push기능을 고정 this이 변수에 저장 될 수 있도록 값. 사실, 7 바이트 식별자 [].push(그러나 할당되지 않음) 그대로없이 사용할 수있다 bind:

[].push(10,2,65,7)
> 4

[].push()
> 0

19

자바 스크립트 (ES6), 16 바이트

(...a)=>a.length


18

하스켈 , 108 (107) 95 94 바이트

class T r where z::Int->r
instance T Int where z=id
instance T r=>T(a->r)where z n _=z$n+1
z 0

온라인으로 사용해보십시오!

이것은 놀랍게도 일하기 어려웠지만 명령형 언어로 사소한 것을 구현하는 방법을 찾으려고 노력했습니다.


젠장, 넌 날 이겼어 바인딩이없는 함수 f라고 말하면 선택 사항 z 0이므로 main = print $ ((z 0) pi 0 () [] :: Int)작동합니다.
Angs

그리고 이는 익명 함수로 사용될 때 형식이 작동하므로 마지막 두 행에서 모든 것을 제거 할 수 있습니다.z 0
Angs

감사합니다! 익명 함수를 테스트 할 때 잘못된 일이 발생했습니다. 귀하의 예를 시험해 보았지만 정상적으로 작동했습니다.
user9549915

::Int에서와 같이 답변 유형을 조만간 선언해야하기 때문에 바이트 수로 계산해야한다고 생각합니다 main = print $ ((z 0 :: Double -> Integer -> () -> [a] -> (Int->Int->Int) -> IO () -> Int) pi 0 () [] (+) main). 또한 이것은 컴파일 타임에만 작동하므로 foldl(\a b->a b) (z 0) $ [1..5])::Int작동하지 않는 것으로 생각합니다 . 어느 쪽이든, 이것은 좋은 물건입니다.
Angs

2
s/imperative/non-curry/
user202729


12

아마 포장되어야하지만 :f(){ echo $#; }
muru

8
@muru 그것은 전체 프로그램으로 괜찮아 보입니다.
Neil

OP는 단지 기능만을 원한다는 것을
Neil

2
@Neil 쉘 스크립트는 기능과 똑같이 작동합니다. OP는 기능이 무엇인지 명확하지 않으며 제출은 디스크에 저장된 기능 일 뿐이라고 주장합니다.
Pavel

9

Brain-Flak , 6 바이트

내 첫 Brain-Flak 솔루션 가치있는 게시, 나는이 직업에 맞는 도구라고 생각합니다.

([]<>)

온라인으로 사용해보십시오!

설명

Brain-Flak 프로그램을 실행할 때 처음에는 왼쪽 스택에 모든 인수가 포함됩니다. 거기에서 그것은 단순히 다음의 문제입니다.

(      -- push the following..
 []    --   height of the stack (ie. # of arguments)
   <>  -- ..to the other stack  (toggles to the other stack)
)      --
       -- the right stack now contains the # of arguments which
       -- gets printed implicitly

7

Wolfram Language (Mathematica) , 11 바이트

Tr[1^{##}]&

온라인으로 사용해보십시오!

JungHwan Min이 추천 함. 일부 제한 (입력은 직사각형이어야 함)이지만 임의의 입력을 처리 할 필요는 없습니다.

11 바이트

Length@!##&

온라인으로 사용해보십시오!

Martin Ender가 제안한 또 다른 11 바이트 솔루션. 입력이 하나도 없지만 여전히 모든 경우에 올바른 값을 반환하는 경우 오류가 발생합니다.

12 바이트

Length@{##}&

온라인으로 사용해보십시오!

내 원래 솔루션.

Mathematica ##에서 함수에서 가변 수의 인수를 나타냅니다. {그리고 }목록에서 그들을 감싸고 Length@리스트의 길이를합니다. &마지막에 이것을 실제 기능으로 바꿉니다.


7

R , 30 바이트

function(...)length(list(...))

온라인으로 사용해보십시오!


1
function(...)nargs()는 20 바이트이지만 length(...), nargs비슷한 기능을 googled 할 때까지 내 초기 접근 방식을 사용했습니다 .
Giuseppe

@ 주세페 흠, 나는 list(...)논리 로 변환을 시도했지만 sum()사용할 수 있지만 까다로운 : /
JAD

1
하하, 그 주장에 나를 미끼려고하지 말아라.;)
RoryT

1
@RoryT 아, 실제로 R 문서는 결합이라고 말합니다. Nevermind : D
JAD

2
...length() 같은 일을length(list(...))
주세페

7

Bash, 12 바이트 (4를 저장 한 paxdiablo 덕분에)

n()(echo $#)

bash 프롬프트에서 복사하여 붙여 넣습니다. 그런 다음 프롬프트에서 n 기능을 실행하십시오.

$ n
0
$ n 46 gr 3443 dad
4
$ n 4fwj23 wrw jdwj 00998 34 eyt q3 vg wq j qw
11

2
PPCG에 오신 것을 환영합니다!
Martin Ender

스크립트가 "./n"이고 기능이 아니라고 말할 수 있습니까? 그런 다음 : echo $#7 바이트입니다. (그런 다음 "./n"스크립트를 시작하기 위해 사용하는 쉘이 될 것이다. 즉, bash를 실행 한 다음 언제 : ./n arg1 ... argnbash에 의해 해석 될 것이다.)
Olivier Dulac

@Olivier Dulac 도전은 분명히 기능을 말합니다.
Wastrel

7

C ++ 14 (gcc) , 34 바이트

일반적인 가변 람다 함수 (C ++ 14 필요) :

[](auto...p){return sizeof...(p);}

온라인으로 사용해보십시오!

이전 (잘못된) 답변 : 32 바이트

누락 된 template<class...T>(p)

int f(T...p){return sizeof...p;}

6
C ++ 14, C ++ 11에는 일반적인 람다가 없습니다.
Quentin

1
당신은 할 수 괄호 주위를 제거 할 수 있도록 허용 플래그를 추가p (및 -w경고를 해제).
nwp

@nwp : -fpermissive그 옵션에 12 바이트 비용이 들지 않습니까? 표준 ISO C ++ 또는 GNU C ++가 아닌 경우
Peter Cordes

@PeterCordes 아마도 명령 행을 통해 프로그램을 전달하여 사소한 0 바이트 솔루션을 피하는 것이 좋습니다. 나는 그것이 모욕적이지 않기 때문에 여기에 대해서는 생각하지 않았습니다.
nwp

@Quentin fixed -> C++14
Bierpfurz


5

Octave, 9 bytes

@()nargin

Try it online!

Anonymous function taking any number of arguments (and silently discarding the lot), and outputs the number of arguments through the built-in nargin. This does not work in MATLAB, where you would need varargin to allow for arbitrary many arguments.



4

Perl 5, 9 bytes

sub{~~@_}

Try it online!


A quick sitewide search of answers seems to indicate that you can leave out the sub
ASCII-only

2
Protip: TIO let's you copy in PPCG post format (ESC, S, G)
ASCII-only

@ASCII-only Oh nice, thanks! :) As for leaving out the sub, I don't think so. It's not a function without it.
Chris

@ASCII-only I'd consider answers without sub invalid since the result isn't something you can call or assign to a variable
Ton Hospel


4

C# .NET, 11 bytes

a=>a.Length

Try it online.

Explanation:

In C# .NET object is used for multi-type arguments, allowing one to pass integers, strings, characters, etc. as possible inputs. For example:

// Can be called like: `F(2)`, `F("test")`, `F('a')`, etc.
void F(object arg){ ... }

C# .NET can also have a fixed size of optional arguments. For example:

// Can be called like: `F()`, `F(2)`, `F("test")`, `F('a')`, etc.
void F(object arg = null){ ... }

And there are also varargs, which is an undefined amount of optional arguments (which is what I've used in this answer). For example:

// Can be called like: `F()`, `F(2)`, `F(2, "test", 'a')`, etc.
void F(params object[] args){ ... }

Usually lambdas are created like this:

System.Func<object[], int> F f = a=>a.Length;
// A call like `f(new object[]{2, "test", 'a'))` will return 3 (size of the input array)

But unfortunately System.Func doesn't support params varargs, so I'll have to create a delegate instead:

delegate int F(params object[] args);
F f = a=>a.Length;
// A call like `f()` will return 0, and `f(2, "test", 'a')` will return 3

Which is my answer for this challenge, and can be found in the linked TIO test code.


The only limitation is that inputting an actual object[] like f(new object[]{1,2,3}) will result in 3 instead of 1. f(new int[]{1,2,3}) will still result in 1, because it interprets the int[] as a single object. To have the object[] parameter be interpret as a single object as well it can be casted to an object like this: f((object)new object[]{1,2,3}).


I have to say, if there were ever an answer that made me support including lambda-related boilerplate in C# answers it would be this one... but it is definitely a valid solution.
Kamil Drakari

@KamilDrakari Maybe it indeed wasn't very clear what I did without opening the TIO-link, so I've added an explanation.
Kevin Cruijssen

1
@Taemyr I tried finding a solution, but unfortunately there is none for C# .NET, except for casting any object[] parameters to object, like this: f((object)new object[]{1,2,3});. There is no way to differentiate between f(new object[]{1,2,3}); and f(1,2,3); as far as I could find.
Kevin Cruijssen

1
this handles array parameters correctly for a huge penalty of bytes. There might be a more concise structure that can handle it, but it works in my testing.
Kamil Drakari

1
@KamilDrakari Hmm, but it fails for f(1, new object[]{1,2,3}) again though. Not sure if a solution for this behavior can be found.
Kevin Cruijssen

4

Dodos, 32 31 bytes

f
	dot i f dab
i
	
	dip dot dab

Try it online!

Uses Dennis' increment function.

Explanation

f                     # definition of f - target function
        dot i f dab   # sum of j(f(all args but first)). recurses until it has 0 args
i                     # definition of i - returns (arg, 1) given 1 arg
                      # arg
        dip dot dab   # 1 (dot dab on list of length 1 returns 0, dip returns |0 - 1|)

Alternatively, 32 bytes without recursion in target function (thanks @Leo)

	dot i
i
	dip dot dab dot
	i dab

Try it online!

Explanation

        dot i             # anonymous function: sum of i(args)
                          # here this becomes implicit main
i                         # definition of i - returns a list with all arguments replaced with 1
        dip dot dab dot   # 1 (dab dot returns empty list, dot returns 0, dip returns |0 - 1|
        i dab             # list concatenated with i(all args but first)

Here's another same-length solution Try it online! I can't seem to understand why yours works though, could you add an explanation please?
Leo

Hey, you added an explanation to my solution! I wanted one for yours, I know how mine works xD
Leo

1
@Leo sorry for late reply, idek what I'm doing, just copied Dennis' function, will try to understand asap. I had no idea how dodos works so I figured out what yours did first
ASCII-only

No worries, it was just a funny situation :)
Leo

@Leo ok so does my explanation make sense? (note: I'm on mobile so feel free to edit it to make it better lol)
ASCII-only

3

C++, 72 bytes

int f(){return 0;}template<class...P>int f(int,P...p){return f(p...)+1;}

Saves bytes by only working with ints.


You can use sizeof....
L. F.

3

Rust, 57 bytes

macro_rules!f{()=>{0};($($x:expr),+)=>{[$($x),+].len()};}

Explanation:

macro_rules! f {         // define a macro called f
    () => {0};           // when called without arguments, expand to 0
    ($($x:expr),+) => {  // when called with 1 or more comma seperated arguments
        [                // rust uses [a, b, c] to make an array
            $($x),+      // expand to the arguments seperated with a comma
        ]                
        .len()           // take the length of that.
    };
}

Test:

fn main() {
    println!("{:?}", f!());                // prints 0
    println!("{:?}", f!(4));               // prints 1
    println!("{:?}", f!(5, 2));            // prints 2
    // works with anything, as long as you dont mix things
    println!("{}", f!("", "a", "hello"));  // prints 3
}




2

PHP, 11 bytes

<?=$argc-1;

Try it online: 1 input | 3 inputs


I'm not so sure about this one (and it's validity) since it is the count of arguments passed to call PHP.
Ismael Miguel

@IsmaelMiguel, see this consensus.
Shaggy

1
The question explicitly requires a function that returns the number, does not display it: "...write a function that takes a variable number of arguments and returns the number of arguments."
axiac

1
Re-quoting the question: "Using your language of choice, write a function that takes a variable number of arguments and returns the number of arguments it was called with.". Your code doesn't contain functions.
Ismael Miguel

@IsmaelMiguel, if that were indeed the case then many other solutions would also be invalidated. The norm is to allow solutions to be programmes or functions.
Shaggy

2

Batch, 50 49 bytes

set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

No builtin in Batch, so we have to go old-school. Saved 1 byte thanks to @IsmaelMiguel. Outputs via exit code, or save 3 bytes if output via global variable is valid. Example of use in a full program:

@echo off
call:c %*
echo %ERRORLEVEL%
exit/b
:c
set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

I believe that this answer is answer goes (somewhat) against the rules. Batch has something somewhat close to functions. You can do something similar to :a|set r=0&for %%a in (%*)do set/ar+=1 (| = windows-style newline). This solution is 38 bytes. To execute it, do call :a <args> with a goto :eof before the function, being the value available inside the variable r. If you want to keep your solution, remove the /a on the first set, and remove those @.
Ismael Miguel

@IsmaelMiguel Like this? (Note: I didn't include the function name in the byte count, but I did include the function return, which seems reasonable, as there needs to be one somewhere.)
Neil

Yes, that's exactly it. Nice catch with the exit code! I was surprised to see that exitcodes can be larger than 255. An example is the list provided by Symantec: symantec.com/connect/articles/…
Ismael Miguel

2

x86 32-bit (i386) machine code function, 13 bytes

Calling convention: i386 System V (stack args), with a NULL pointer as a sentinel / terminator for the end-of-arg-list. (Clobbers EDI, otherwise complies with SysV).

C (and asm) don't pass type info to variadic functions, so the OP's description of passing integers or arrays with no explicit type info could only be implemented in a convention that passed some kind of struct / class object (or pointers to such), not bare integers on the stack. So I decided to assume that all the args were non-NULL pointers, and the caller passes a NULL terminator.

A NULL-terminated pointer list of args is actually used in C for functions like POSIX execl(3): int execl(const char *path, const char *arg, ... /* (char *) NULL */);

C doesn't allow int foo(...); prototypes with no fixed arg, but int foo(); means the same thing: args unspecified. (Unlike in C++ where it means int foo(void)). In any case, this is an asm answer. Coaxing a C compiler to call this function directly is interesting but not required.

nasm -felf32 -l/dev/stdout arg-count.asm with some comment lines removed.

24                       global argcount_pointer_loop
25                       argcount_pointer_loop:
26                               .entry:
28 00000000 31C0             xor   eax, eax  ; search pattern = NULL
29 00000002 99               cdq             ; counter = 0
30 00000003 89E7             mov   edi, esp
31                       ;    scasd           ; edi+=4; skip retaddr
32                       .scan_args:
33 00000005 42               inc   edx
34 00000006 AF               scasd            ; cmp eax,[edi] / edi+=4
35 00000007 75FC             jne  .scan_args
36                       ;    dec   edx       ; correct for overshoot: don't count terminator
37                       ;    xchg  eax,edx
38 00000009 8D42FE           lea   eax, [edx-2]    ; terminator + ret addr
40 0000000C C3               ret

size = 0D               db $ - .entry

The question shows that the function must be able to return 0, and I decided to follow that requirement by not including the terminating NULL pointer in the arg count. This does cost 1 byte, though. (For the 12-byte version, remove the LEA and uncomment the scasd outside the loop and the xchg, but not the dec edx. I used LEA because it costs the same as those other three instructions put together, but is more efficient, so the function is fewer uops.)

C caller for testing:

Built with:

nasm -felf32 -l /dev/stdout arg-count.asm | cut -b -28,$((28+12))- &&
 gcc -Wall -O3 -g -std=gnu11 -m32 -fcall-used-edi arg-count.c arg-count.o -o ac &&
 ./ac

-fcall-used-edi is required even at -O0 to tell gcc to assume that functions clobber edi without saving/restoring it, because I used so many calls in one C statement (the printf call) that even -O0 was using EDI. It appears to be safe for gcc's main to clobber EDI from its own caller (in CRT code), on Linux with glibc, but otherwise it's totally bogus to mix/match code compiled with different -fcall-used-reg. There's no __attribute__ version of it to let us declare the asm functions with custom calling conventions different from the usual.

#include <stdio.h>

int argcount_rep_scas();       // not (...): ISO C requires at least one fixed arg
int argcount_pointer_loop();   // if you declare args at all
int argcount_loopne();

#define TEST(...) printf("count=%d = %d = %d   (scasd/jne) | (rep scas) | (scas/loopne)\n", \
        argcount_pointer_loop(__VA_ARGS__), argcount_rep_scas(__VA_ARGS__), \
        argcount_loopne(__VA_ARGS__))

int main(void) {
    TEST("abc", 0);
    TEST(1, 1, 1, 1, 1, 1, 1, 0);
    TEST(0);
}

Two other versions also came in at 13 bytes: this one based on loopne returns a value that's too high by 1.

45                       global argcount_loopne
46                       argcount_loopne:
47                           .entry:
49 00000010 31C0             xor   eax, eax  ; search pattern = NULL
50 00000012 31C9             xor   ecx, ecx  ; counter = 0
51 00000014 89E7             mov   edi, esp
52 00000016 AF               scasd           ; edi+=4; skip retaddr
53                       .scan_args:
54 00000017 AF               scasd
55 00000018 E0FD             loopne  .scan_args
56 0000001A 29C8             sub   eax, ecx
58 0000001C C3               ret

size = 0D = 13 bytes               db $ - .entry

This version uses rep scasd instead of a loop, but takes the arg count modulo 256. (Or capped at 256 if the upper bytes of ecx are 0 on entry!)

63                       ; return int8_t maybe?
64                       global argcount_rep_scas
65                       argcount_rep_scas:
66                               .entry:
67 00000020 31C0             xor   eax, eax
68                           ;    lea   ecx, [eax-1]
69 00000022 B1FF             mov   cl, -1
70 00000024 89E7             mov   edi, esp
71                       ;    scasd              ; skip retaddr
72 00000026 F2AF             repne scasd        ; ecx = -len - 2 (including retaddr)
73 00000028 B0FD             mov   al, -3
74 0000002A 28C8             sub   al, cl       ; eax = -3 +len + 2
75                       ;    dec   eax
76                       ;    dec   eax
77 0000002C C3               ret

size =  0D = 13 bytes         db $ - .entry

Amusingly, yet another version based on inc eax / pop edx / test edx,edx / jnz came in at 13 bytes. It's a callee-pops convention, which is never used by C implementations for variadic functions. (I popped the ret addr into ecx, and jmp ecx instead of ret. (Or push/ret to not break the return-address predictor stack).



1

JavaScript, 35 bytes

f=
function(){return arguments.length}

console.log(f(2,5,4))


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.