Легенда заполняется колонками слева направо. Другими словами, если вы обманом поймете, что есть еще одна строка (без текста или цвета строки в легенде), вы можете заполнить пространство под «plot 3».
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
X = np.linspace(0,100,11)
plt.plot(X,-X, label='plot 1', color='red')
plt.plot(X,-2*X, label='plot 2', color='green')
plt.plot(X,-3*X, label='plot 3', color='blue')
line1 = Line2D(range(10), range(10), marker='', color="red")
line2 = Line2D(range(10), range(10), marker='',color="green")
line3 = Line2D(range(10), range(10), marker='', color="blue")
line4 = Line2D(range(10), range(10), marker='', color="white")
plt.legend((line1,line4, line3,line2),('plot1','','plot3','plot2'),numpoints=1, loc=4,ncol=2)
plt.show()
Во-первых, K&R имеют ошибку на данном отрывке:
117(§5.10): В примере найдите, инкременты программы
argv[0]
. Это не запрещено, но и не разрешено.
Теперь объяснение.
Допустим, ваша программа называется prog
, и вы выполняете ее с помощью: prog -ab -c Hello World
. Вы хотите иметь возможность разобрать аргументы, чтобы сказать, что опции a
, b
и c
были указаны, а Hello
и World
- это аргументы без опций.
argv
имеет тип char **
- помните, что параметр массива в функции тот же самый, что и указатель. При вызове программы все выглядит так:
+---+ +---+---+---+---+---+
argv ---------->| 0 |-------->| p | r | o | g | 0 |
+---+ +---+---+---+---+---+
| 1 |-------->| - | a | b | 0 |
+---+ +---+---+---+---+
| 2 |-------->| - | c | 0 |
+---+ +---+---+---+---+---+---+
| 3 |-------->| H | e | l | l | o | 0 |
+---+ +---+---+---+---+---+---+
| 4 |-------->| W | o | r | l | d | 0 |
+---+ +---+---+---+---+---+---+
| 5 |-------->NULL
+---+
Здесь argc
равняется 5, а argv[argc]
равняется NULL
. В начале argv[0]
- это char *
, содержащий строку "prog"
.
В (*++argv)[0]
из-за круглых скобок argv
сначала инкрементируется, а затем разыменовывается. Эффект инкремента заключается в том, что стрелка argv ---------->
стрелка "на один блок вниз" указывает на 1
. Эффект разыменования заключается в получении указателя на первый аргумент командной строки, -ab
. Наконец, берем первый символ ([0]
в (*++argv)[0]
) этой строки и проверяем, не является ли он '-'
, так как это обозначает начало опции.
Для второй конструкции, на самом деле, мы хотим пройти по строке, на которую указывает текущий указатель argv[0]
. Итак, нам нужно трактовать argv[0]
как указатель, игнорировать его первый символ (то есть '-'
, как мы только что тестировали), и смотреть на другие символы:
++(argv[0])
увеличит argv[0]
, чтобы получить указатель на первый не -
символ, и разыменование даст нам значение этого символа. Итак, мы получаем *++(argv[0])
. Но так как в языке Си []
связывает более плотно, чем ++
, то на самом деле мы можем избавиться от скобок и получить наше выражение в виде *++argv[0]
. Мы хотим продолжить обработку этого символа до тех пор, пока он не станет 0
(последним символом в каждой строке на рисунке выше).
Выражение
c = *++argv[0]
присваивает c
значение текущей опции, а имеет значение c
. Строка while(c)
является сокращением для while(c != 0)
, поэтому строка while(c = *++argv[0])
в основном присваивает значение текущей опции c
и тестирует его, чтобы посмотреть, достиг ли мы конца текущего аргумента командной строки.
В конце этого цикла аргумент argv укажет на первый аргумент без опции:
+---+ +---+---+---+---+---+
| 0 |-------->| p | r | o | g | 0 |
+---+ +---+---+---+---+---+
| 1 |-------->| - | a | b | 0 |
+---+ +---+---+---+---+
| 2 |-------->| - | c | 0 |
+---+ +---+---+---+---+---+---+
argv ---------->| 3 |-------->| H | e | l | l | o | 0 |
+---+ +---+---+---+---+---+---+
| 4 |-------->| W | o | r | l | d | 0 |
+---+ +---+---+---+---+---+---+
| 5 |-------->NULL
+---+
Помогает ли это?
.да, вы правы.
while(--argc > 0 && (*++argv)[0] == '-')
сканирует массив аргументов командной строки (длины Argc) один за другим в поисках аргументов, начинающихся с префикса опции -
. Для каждого из них:
while(c = *++argv[0])
сканирует набор символов переключателя, который следует за первым -
в текущем аргументе (т.е. t
и n
в -tn
, до тех пор, пока не попадает на нулевой терминатор строки \0
, который завершает цикл while, так как вычисляется как false.
Такая конструкция позволяет работать как
myApp -t -n
, так и
myApp -tn
и то, и другое и понимается как имеющие опции t
и n
.
Инкрементирование аргумента - очень плохая идея, так как после этого трудно вернуть исходное значение. Проще, понятнее и лучше использовать целочисленный индекс - ведь argv - это массив!
Для ответа на вопрос ++argv увеличивает указатель. Затем к нему применяется индендикация для получения первого символа.
Да, оба выражения различаются (хотя и незначительно). IMO, этот код немного чересчур умён. Лучше было бы с чем-то вроде этого:
for (int i=1; i<argc; i++)
if (argv[i][0] == '-') {
size_t len = strlen(argv[i]);
for (int j=0; j<len; ++j)
switch(argv[i][j]) {
case 'x':
// ...
Это практически эквивалентно приведенному выше коду, но я сомневаюсь, что кому-то (кто вообще знает C) будет сложно разобраться, что он на самом деле делает.
.Круглые скобки изменяют порядок, в котором оцениваются выражения.
Без скобок *++argv[0]
:
argv[0]
получает указатель на символьные данные, на которые в данный момент указывает argv
. ++
увеличивает указатель на следующий символ в символьном массиве. *
получает символ. в круглых скобках (*++argv)[0]
:
++argv
увеличивает указатель argv для указания на следующий аргумент. *
присваивает ему значение, чтобы получить указатель на символьные данные. [0]
получает первый символ в символьном массиве.