LINUX.ORG.RU

Демонстрация массива и цикла по нему

 ,


0

2

Учитывая не малое число откликов и отсутствие возражний по теме простейшей программы продолжаем, с учётом замечаний и идей…

Задание к следующему конкурсу звучит примерно так:

Написать на любимом языке программу, которая демонстрирует использование массива данных и применяет по ней цикл.

Условия:

  1. ясность, наглядность, понимаемость и простота восприятия
  2. минимализм, краткость не в ущерб наглядности и пункту выше

Начну со своего любимого Си , опять-таки с учётом полученного опыта и «набитых шишек».

Почему многие преподаватели и авторы книг, постов в блогах демонстрируют в качестве «пробы пера» дополнительные вводные переменные из массива и переменных цикла по нему, когда это загромождает код и усложняем новичком его восприятие.

Все рассказывают, про функцию main в Си, но молчат (видимо, откладывая на потом), что в её заголовке уже есть в качестве параметров и скалярная переменная argc и, логически связанный с ней, массив строк argv. Их ведь сразу можно использовать! Но почему же эти педагоги от программирования сразу не объясняют назначение и смысл этих переменных? Ведь всё равно придётся это проходить. Не проще ли их сразу начать использовать для демонстрации возможностей языка? И по ходу дела рассказать про циклы, логические выражения и условия в операторах…

У меня получилось всего 5 строчек простого и наглядного кода на Си. Вот текст файла arr_cycl.c:

#include <stdio.h>
void main(int argc, char* argv[]){
        for(argc-- ; argc>=0 ; argc--)
                printf("argv[%i]=%s\n" , argc, argv[argc]);
}

Компиляция и демонстрационный прогон:

gcc arr_cycl.c && ./a.out раз два три
argv[3]=три
argv[2]=два
argv[1]=раз
argv[0]=./a.out

Итерационный цикл с уменьшением переменной, которая и определяет фактически крайний наибольший по индексу элемент, использован чтобы не вводить дополнительных переменных, а нижняя граница массива argv строго табулирована нулём.

Тут можно многое рассказывать новичку в программировании про его нюансы: например, что наибольший индекс (крайний элемент) - это размер массива за вычетом 1, потому, что наименьший индекс - это нуль (нулевое смещение в Си). И это условие выполняется в первой части оператора for

Жду нечта аналогичного на других языках с разъяснениями. Даёшь простые и понятные альтернативы набившим оскомину заскорузлым подходам к изучению программирования (пробам пера)!

P.S.:

Кстати, наш насущный bash - тоже интересен тем, что там, в отличие от классического Bourne Shell - есть массивы.


простого и наглядного кода

for(argc-- ; argc>=0 ; argc--)

/0

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
import System.Environment

main = do
  p <- getProgName
  r <- getArgs
  print $ p:r
$./array 1 2 3
["array","1","2","3"]

Код у тебя не наглядный ни капли.Переменные argc и argv в туториалах не используют, в т.ч. потому, что на Windows придется запускать ущербный cmd.exe, чего никто не делает, все запускают двойным кликом по бинарнику. В специальных олимпиадах по написанию хелловорлдов участвовать неинтересно, хочешь набить себе кармы – постарайся хоть нормальную задачу придумать.

Итерационный цикл с уменьшением переменной, которая и определяет фактически крайний наибольший по индексу элемент, использован чтобы не вводить дополнительных переменных, а нижняя граница массива argv строго табулирована нулём.

Ну и пиши русским языком, ты не зачет сдаешь здесь.

Siborgium ★★★★★
()

Кстати, наш насущный bash - тоже интересен тем, что там, в отличие от классического Bourne Shell - есть массивы.

Shell-ы предназначены для запускания внешних программ, с аргументами. Потому средства работы с аргументами там были изначально, без всяких массивов.


while true; do
        eval a=\$$#
        echo "argv[$#]=$a"
        [ $# -eq 0 ] && break
        shift
done

vodz ★★★★★
()

Подучи поинтеры, и жизнь твоя станет шелковистая. ;)

#include <stdio.h>

int
main(int argc, char **argv)
{
	while (*argv) {
		printf("argv=%s\n", *argv++);
	}
	return 0;
}
beastie ★★★★★
()

Жду нечта аналогичного на других языках

powershell

$i = 'a','b','c'   
$ind = $i.count    
for ($ind--; $ind -ge 0; $ind--) { $i[$ind] }

<#
c
b
a
#>
anonymous
()

которая демонстрирует использование массива данных и применяет по ней цикл

по ней

from urllib.request import urlopen

pony_names_url = "https://raw.githubusercontent.com/ahoydave/pony_names/master/pony_names.txt"
pony_names = urlopen(pony_names_url).read().decode().split('\n')

ponie_pictures = [f"Picture of {pony}" for pony in pony_names]

lyrics = """
    %i pictures of ponies on the wall,
    If one of those pictures should happen to fall, 
    There'll be %i pictures on the wall... 
"""

while ponie_pictures:
    print("Ponie pictures:", ponie_pictures)
    n = len(ponie_pictures)
    print(lyrics % (n, n-1)) 
    print(ponie_pictures.pop(), "has fell.")

print("No more pictures of ponies on the wall.")
$ python ponies.py
Ponie pictures: ['Picture of Applejack', 'Picture of Pinkie Pie', ...

    326 pictures of ponies on the wall,
    If one of those pictures should happen to fall, 
    There'll be 325 pictures on the wall... 

Picture of Tropical Storm has fell.
Ponie pictures: ['Picture of Applejack', 'Picture of Pinkie Pie', ...

    325 pictures of ponies on the wall,
...
anonymous
()

nodejs:

process.argv.forEach(v=>console.log(v))
$ node main.js a b c d
/opt/nodejs/bin/node
/home/user/main.js
a
b
c
d

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от anonymous
#!/usr/bin/pwsh -noprofile

$i = $args
[array]::Reverse($i)
$i
anonymous
()

Учитывая не малое число откликов и отсутствие возражний по теме

bikeshedding

theNamelessOne ★★★★★
()
Ответ на: комментарий от beastie

Подучи поинтеры, и жизнь твоя станет шелковистая. ;)

Я думал, это третьей переменной envp касается только (массив переменных среды окружениея - Environment Pointer).

Но ты меня обыграл. Код ещё короче, проще и понятней стал:

#include <stdio.h>
void main(int argc, char* argv[]) {
        while (*argv)
                printf("argv=%s\n", *argv++);
}

В выводе правда исчез порядковый номер переменных. Но оное и не требовалось условиями конкурса…

./a.out раз два три четыре
argv=./a.out
argv=раз
argv=два
argv=три
argv=четыре

Господа, а можно хотя бы в заголовках писать, что за язык вы используете, как его запускать, собирать?

Или это тоже - такое для смотрящего код упражнение: понять язык и смочь его запустить?

Особенно Haskell порадовал в этом плане… Go я когда-то пробовал - и он себя по fmt выдаёт. А вот import - это какой-то не оригинальный бич языкового времени…

Android
() автор топика
Ответ на: комментарий от Android

Только вот void main – неправильно. По стандарту должно быть int main.

beastie ★★★★★
()

Луа

for i = -1, #arg
do
  print("argv[" .. i .. "] = ", arg[i])
end
$ lua пробег_по_массиву.lua первый второй
argv[-1] = 	lua
argv[0] = 	пробег_по_массиву.lua
argv[1] = 	первый
argv[2] = 	второй
AKonia ★★
()
Последнее исправление: AKonia (всего исправлений: 2)
Ответ на: комментарий от Android

Полный пример одного моего старенького простенького cgi «скрипта», которым я даже когда-то пользовался: https://git.dim13.org/cgi.git/tree/env/env.c

Пример выхлопа: https://www.dim13.org/cgi-bin/env

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: Ничто не ново под луной от Kroz

Код-гольфинг vs Демонстрация новичкам

Ребята там соревнуются друг перед другом в профессионализме краткости. Нагуглил - это Код-Гольфинг называется.

А тут мы всё-таки пытаемся продемонстрировать незнайкам ясность того или иного любимого языка. И краткость - это не цель, а следствие. Я вот помню были такие на Perl однострочники с регулярными выражениями. Они делали много, но понять их невозможно новичку без спец. подготовки.

Пока по краткости проходов массивов на первых местах JavaScript на Node-платформе. Но можно пояснить, что там за суть конструкции: v=>console.log(v) ? элемент итерации назначить переменной v, чтобы потом её выводить в консольный лог?

Android
() автор топика
Ответ на: комментарий от Android

Ты уж определись в каком порядке выводить аргументы. В шапке в обратном порядке, здесь по порядку.

anonymous
()
Ответ на: комментарий от AKonia

Lua

У Lua интересный и вроде совместимый по концепции параметров код. Имя скрипта в нулевом элементе массива аргументов, как принято. Но и про сам интерпретатор (его программу) не забыли - индекс идёт в обратную зону (отрицательных значений…)…

Android
() автор топика
Ответ на: Lua от Android

ещё с учётом того, что принято что в луа массивы с 1, то и из arg можно извлекать непосредственно пользовательские параметры с помощью встроенного итератора ipairs, т.е.

for i,v in ipairs(arg)
do
  print("argv["..i.."] = " .. v)
end
-- или так
for i,v in ipairs(table.pack(...))
do
  --//--
end

получая

$ lua пробег_по_массиву.lua первый второй
argv[1] = 	первый
argv[2] = 	второй
AKonia ★★
()
Последнее исправление: AKonia (всего исправлений: 3)

Это не обучение, это понт десятиклассника перед девятиклассником - как сам обучался уже забыл, как обучать не понимаешь, практики по языку нет. За такую конструкцию в прод коде уже можно увольнять, а ты кого-то учить собрался. Понятно что оба твоих пункта она проваливает.

Во-первых, ты не показываешь самого главного - как объявлять массив. Во-вторых, мешаешь сразу кучу сущностей: кроме массива тут участвует его размер в виде отдельной переменной, функция, аргументы функции, передача аргументов из командной строки. А в твоём случае ещё и ломающий мозг обратный цикл, чреватый фатальной ошибкой в случае беззнакового аргумента, деструкция argc…

Нет, только «заскорузлым» способом:

int a[] = {1,2,3};
printf(... a[0], a[1], a[2]);
printf(... sizeof(a), sizeof(a[0]), sizeof(a)/sizeof(a[0]));
for (size_t i = 0; ...)
a = [1,2,3]
print(a[0], a[1], a[2], a[-1]);
print(a[3])
print(len(a))
for v in a:
    print(v)
slovazap ★★★★★
()

Си

не использовать секретный оператор

лан

#include <stdio.h>
int main(int argc, char* argv[]){
  while(argc --> 0)
    printf("argv[%i]=%s\n", argc, argv[argc]);
}
Kuzy ★★★
()

ruby

ruby -e "ARGV.each {|x| p x}" раз два три
"раз"
"два"
"три"
ino
()

побуду за ассемблерошизика

writev=20
exit_group=231
STDOUT_FILENO=1
.text
.globl main
main:
movdqa iovec,%xmm0   #iovec
movq   %rsp,%r8      #iovec[] (stack)
movl   %edi,%edx     #argc niovec
shll   $1,%edx       #2 iovec per line
xorb   %al,%al       #repnz cmp to
movq   $-1,%rcx      #repnz max
loop:
movq   (%rsi),%rdi   #argv** repnz ptr
movq   %rdi,(%r8)    #iovec.iov_base*
repnz scasb          #strlen
subq   (%rsi),%rdi   #strlen
decq   %rdi
movq   %rdi,8(%r8)   #iovec.iov_len
movdqu %xmm0,16(%r8) #iovec
addq   $32,%r8
addq   $8,%rsi
cmpq   $0,(%rsi)
jnz    loop
movq   %rsp,%rsi     #iovec[]
movl   $STDOUT_FILENO,%edi #fd
movl   $writev,%eax
syscall
movl   $exit_group,%eax
syscall
.section .data.rel.ro
.align 16
iovec:.quad endl,1 #iovec{.iov_base*,.iov_len}
endl:.byte '\n'


$ gcc -no-pie snip.s &&./a.out 1 22 333 4 
./a.out
1
22
333
4
anonymous
()

Оффтопик

Надо, чтобы программа сама себя печатала на экран. Тогда будет интересно.

Вот один из вариантов:

0000000 00b8 8eb8 b8d8 0000 c08e a0bb b80f 0200
0000010 0789 eb83 7502 b9f9 0016 00bb bd00 7cac
0000020 02b4 468a 4500 0789 c383 e202 baf5 0001
0000030 00bd 897c b8d3 00a0 e3f6 c389 d089 c148
0000040 04e0 3ce8 c600 3a07 c383 b904 0008 8b26
0000050 0046 2ce8 8300 02c3 c583 2602 468a e200
0000060 42ed fa83 7218 d1cc baeb 03d4 0fb0 feee
0000070 88c2 eed8 cafe 0eb0 feee 88c2 eef8 ebf4
0000080 51fd b956 0004 c689 4951 0574 e8c1 eb04
0000090 59f8 0f24 0a3c 0473 3004 02eb 3704 02b4
00000a0 0789 c383 8902 e2f0 5edf c359 7250 6e69
00000b0 2074 7469 6573 666c 6120 646e 6820 6c61
00000c0 3a74 0000 0000 0000 0000 0000 0000 0000
00000d0 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200

Запускать с помощью qemu

luke ★★★★★
()
Ответ на: комментарий от Kuzy

Два оператора в одном выражении в Си

while(argc --> 0)

Уважаемый, не вводите, пожалуйста, зрителей в заблуждение. По-сути, здесь два оператора в условном выражении while() внутри круглых скобок:

  1. выражение > 0 - сравнение с нулём переменной
  2. argc-- - операция постдекремента (на единицу)

То есть наглядней записать так:

while(argc-- > 0)

Но Вы меня тоже обыграли: это изящней оператора for, принимающего три выражения вместо Вашего одного (хоть и не тривиального в while).

Android
() автор топика
$it = new ArrayIterator([0, 1, 2, 3]);
$it->rewind();
while ($it->valid()) {
  echo $it->key(), $it->current(), PHP_EOL;
  $it->next();
}
anonymous
()
Ответ на: комментарий от beastie

Go прям по кайфу, не понимаю хейтеров. Сила и красота видна даже на таких примитивных примерах.

P.S. Зашел в тред найти или оставить этот код.

WitcherGeralt ★★
()
Последнее исправление: WitcherGeralt (всего исправлений: 1)

В паскалях argv не массив, поэтому эквивалент по функциональности:

var i: Integer;
begin
  for i:=ParamCount downto 0 do
    WriteLn(ParamStr(i));
end.

Итерация по массиву в прямом порядке тривиальна:
  for e in a do WriteLn(e);

bormant ★★★★★
()
Ответ на: комментарий от anonymous

вместо размерных постфиксов movl movq(и для остальных команд тоже) можно писать просто mov, там где из аргументов ясен размер. всё таки at&t синтаксис чуть менее всратый, чем я думал

anonymous
()

Python

import sys

for i, s in enumerate(reversed(sys.argv)):
    print(f'argv[{i}]={s}')

Rust (на самом деле, можно делать цикл сразу по args() вместо array.iter() но тогда будет без массива).

fn main() {
    let array: Vec<String> = std::env::args().collect();
    for (i, s) in array.iter().rev().enumerate() {
        println!("argc[{}]={}", i, s);
    }
}
DuMOHsmol
()
Ответ на: Оффтопик от luke

Программа, печатающая себя

Лука, топик «Программа, печатающая себя» - заявка на следующий конкурс-демонстрацию или там вывод номера своего процесс-ID, исходный текст (если есть логика в именовании)…

Опять-таки ясность, понятность, изящество, красота, минимализм… приветствуются.

А скряги, которые «только за деньги на продакшн», видимо с рассчётом, что кто-то из их наёмных халуёв будет эти короткие примеры дописывать в то, на чём они бы хотели наварить - пусть не тратят своё драгоценное … - на форуме полно тем с подобной мотивацией (могут и свою начать).

То же самое относится и к учителям, которые считают, что должна быть только одна методика обучения, лучший и стандартный учебник… Подобные учителя высмеиваются в видео Iron Maiden Can I Play With Madness (погуглите - не пожалеете).

Лука, начинай новую тему - я подключусь если успею…

Android
() автор топика
Ответ на: Код-гольфинг vs Демонстрация новичкам от Android

элемент итерации назначить переменной v, чтобы потом её выводить в консольный лог?

Да. Просто запихать console.log нельзя, у функции 3 аргумента и они все уйдут туда.

crutch_master ★★★★★
()

try python, Luke!

import sys
for i in sys.argv: print i

А вот за такую экономию

for(argc-- ; argc>=0 ; argc--)

надо бить канделябром по пальцам.

AntonI ★★★★★
()
Ответ на: Оффтопик от luke

Спасибо, интересный пример. Что он делает? На исходник бы взглянуть и как запустить: краткая инструкция.

anonymous
()
Ответ на: комментарий от anonymous

iovec[]

writev

прикольно. только не до конца въехал как \n подставляется.

anonymous
()
Ответ на: комментарий от anonymous

А это и есть исходник, иначе задание будет невыполненным. Надо каким-то образом запихать это в бинарь self-print.bin и запустить с qemu-system-i386 -fda self-print.bin.

А вот если удастся воткнуть туда дизассемблер… тогда можно будет публиковать уже и ассемблерный исходник.

luke ★★★★★
()
Ответ на: комментарий от luke

Надо каким-то образом запихать это в бинарь

xxd -r self-print.dump > self-print.bin

и запустить

А вот это вот не работает.

PS: с xxd endianness неправильный – т.е. оно само себя неправильно печатает ;)

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от anonymous

с выводом номеров аргументов на сишный манер

import os
for i in countdown(paramCount(), 0): echo "argv[", i, "]=", paramStr(i)
anonymous
()
#include <stdio.h>

int main(int argc, char *argv[])
{
    puts("---v1---");
    for (int i = 0; i < argc; ++i)
    {
        printf("[%i] -> %s\n", i, argv[i]);
    }

    puts("---v2---");
    while(argc--)
    {
        printf("[%i] -> %s\n", argc, argv[argc]);
    };

    return 0;
}
dron@gnu:~$ gcc lor.c ;./a.out раз два три
---v1---
[0] -> ./a.out
[1] -> раз
[2] -> два
[3] -> три
---v2---
[3] -> три
[2] -> два
[1] -> раз
[0] -> ./a.out
dron@gnu:~$ 
LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.