Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

В чем различия между Git и SVN?

#git #svn #контроль_версий


Вопрос, собственно выбора. Чем Git лучше или хуже SVN? Почему чаще от разработчиков
я слышу именно про Git и его удобства, чем про SVN, хотя как по мне, все что слышал
можно делать и в SVN.

Причём для обоих вариантов есть удобный Tortoise, что сводит общение с репозиториями
практически на один уровень.

Может просто что-то плохо рекламируют, а разницы вообще нет?
    


Ответы

Ответ 1



GIT распределяется, а SVN - нет. Другими словами, если есть несколько разработчиков работающих с репозиторием у каждого на локальной машине будет ПОЛНАЯ копия этого репозитория. Разумеется есть и где-то и центральная машина, с которой можно клонировать репозиторий. Это напоминает SVN. Основной плюс в том, что если вдруг у вас нет доступа к интернету, сохраняется возможность работать с репозиторием. Потом только один раз сделать синхронизацию и все остальные разработчики получат поолную историю. GIT сохраняет метаданные изменений, а SVN целые файлы. Это экономит место и время. Система создания branches, versions и прочее в GIT и SVN отличаются значительно. В GIT проще переключатся с ветки на ветку, делать merge между ними. В общем GIT я нахожу немного проще и удобнее, но бывают конечно иногда сложности. Но где их не бывает? Разумеется есть гораздо больше отличий, но я перечислил те, которые чаще всего встречаются при работе с репозиториями и на МОЙ взгляд наиболее важные.

Ответ 2



Git сложнее для освоения. Лично мне git предпочтительнее, позволяет мне проще сделать больше разного. Например реорганизовать порядок и содержание коммитов. Работа с ветками имхо удобнее и быстрее, через git stash удобно проверять перед коммитом что не забыл добавить вновь созданный файл. Удобнее переносить свои наработки в разные ветки - создать локально ветку, в ней вести разработку, потом просто сделать merge ветки, в svn же нужно просматривать лог на предмет нужных коммитов и переносить их ручками.

Ответ 3



Так же распределённость git может быть полезна при восстановлении данных. Был случай когда накрылся жесткий диск на сервере, и все удалось восстановить благодаря локальным копиям. (P.S конечно лучше иметь зеркальные RAID, но дно другого не исключает).

Ответ 4



Сначала у нас контора работала на svn без веток, потом начали активно использовать гит с ветками, сейчас я устроился в другую контору которая работает на svn с ветками, и если честно каждый раз когда приходится сливать ветки - я с такой грустью и тоской вспоминаю гит, как там всё было просто и удобно, интуитивно понятно, и как кропотливо приходится трястись над каждым конфликтом, над каждым изменённым файлом в обеих ветках, когда гит большую часть умел разруливать сам. А заливание каталогов в svn вообще убивает если честно.

Ответ 5



Я бы в качестве одного из краеугольных отличий назвал бы еще lock http://svnbook.red-bean.com/en/1.7/svn.advanced.locking.html git удобнее тем, что не надо бегать с криком "уберите замок пожалуйста". С другой стороны, встает ряд дополнительных вопросов с merge кода, который впрочем при должном навыке с помощью инструментов git нормально решается.

CharSequence или String?

#java #android #строки


Подскажите, в чем принципиальное отличие между этими объектами? В каких случаях оптимально
использовать CharSequence?
    


Ответы

Ответ 1



CharSequence это интерфейс со всеми вытекающими. Под капотом может быть CharBuffer, Segment, String, StringBuffer, StringBuilder. Или собственная реализация. Чтобы было понимание приведу следующий пример: public class Main { public static void main(String[] args) { String str = new String("Hello from String"); soutString(str); } public static void soutString(String str) { System.out.println(str.getClass()); System.out.println(str); } } Метод SoutString выводит класс и содержимое строки: class java.lang.String Hello from String Если мы захотим использовать StringBuilder, то для того что бы воспользоваться методом SoutString мы должны использовать преобразование. Так как он ожидает String: StringBuilder sb = new StringBuilder("Hello from StringBuilder"); soutString(sb.toString()); Добавляем эти строки и смотрим результат: class java.lang.String Hello from String class java.lang.String Hello from StringBuilder Мы так же знаем, что String и StringBuilder реализуют интерфейс 'CharSequence' и это позволяет нам написать следующее: public class Main { public static void main(String[] args) { CharSequence str = new String("Hello from String"); soutCharSequence(str); CharSequence sb = new StringBuilder("Hello from StringBuilder"); soutCharSequence(sb); } public static void soutCharSequence(CharSequence ch) { System.out.println(ch.getClass()); System.out.println(ch); } } Вывод будет следующим: class java.lang.String Hello from String class java.lang.StringBuilder Hello from StringBuilder Отметим тот факт, что во втором случае в метод был передан StringBuilder. Мы можем передавать в наш метод любые классы, которые реализуют интерфейс. Внутри метода можно использовать методы интерфейса, например length(); А еще мы можем внутри метода безопасно выполнить специфические для объекта действия: if (ch instanceof String) { System.out.println(((String) ch).toUpperCase()); } Я не знаток Android, но думаю, что @Barmaley хотел сказать что если передается в качестве CharSequence объект String, то будет выводится просто текст. А если объект дополнительно реализует Spannable, то его еще можно и раскрасить.

Ответ 2



Формально конечно CharSequence это интерфейс, но смысловая нагрузка CharSequence в Android обычно предполагает, что набор символов может иметь в дальнейшем некие стили - конкретно Spannable, ну например, можно сделать чтобы часть строки имел другой цвет или шрифт. Соответственно CharSequence имеет смысл использовать, если предполагается, что частям строки нужно/можно придавать определенные специальные свойства - то есть то чего нельзя добиться лобовым использованием String.

Ответ 3



CharSequence - это любой набор символов, не обязательно иммутабельный, как объект String. В этом интерфейсе определено буквально 4 метода. В android его реализуют как различные строки и их билдеры (String, StringBuilder, и т.п.), так и текст с форматированием (Spanned). Любой CharSequence можно преобразовать в строку методом .toString(), но это может быть преобразование с потерей информации. Например, если преобразовать в строку текст с форматированием (Spanned), то из него удалится всё форматирование, а если преобразовать StringBuilder, то потеряется возможность редактирования этой строки. Кроме того, такое преобразование — это почти всегда создание нового объекта. Использовать лучше везде, где достаточно 4 методов интерфейса CharSequence и не нужны возможности класса String, либо нужно сохранить свойства переданного объекта при передаче куда-то дальше. Но нужно быть осторожным. В частности, лучше не использовать метод equals() для сравнения объектов, так как он может быть не определен, либо определен не так, как вы хотели бы.

Запуск низкоуровневых программ

#c++ #c #ассемблер #x86 #bios


Пытаюсь разобраться с низкоуровневым программированием. Поставил задачу — написать
"Hello World"-программу, записать её на флешку, перезагрузиться, запустить программу
с флешки (не запуская ОС). 


Можно ли это сделать на С или С++?
Как дать понять биосу, что её надо загрузить в память, выполнить?
Что из себя должна представлять программа? Исполняемый файл .com? .bin файл? Слышал,
это должно быть 16-bit приложение.
Если ли какие-то ограничения в названии программы?
Немного не по теме: Стоит ли пользоваться MASM? Какой Ассемблер сейчас наиболее популярен
для x86?


Заранне Спасибо!
    


Ответы

Ответ 1



Можно и на C/C++, но в очень урезанном виде - практически никаких библиотек. К вашим услугам только функции BIOS (int 13h) (Т.е. ваш "Hello, world" не должен выводиться ни с помощью puts или printf, ни тем более в cout). Никакой main в качестве входной точки (если только не измените соответствующим образом рантайм загрузчик). Т.е. только какие-то базовые вещи (типа арифметики :)), которые компилируются в .obj, и линкуются с начальным загрузчиком на ассемблере. Когда-то интереса ради урезал Turbo C++ до такого состояния - очень даже ничего получалось :) Я в свое время использовал TASM - отлично подходил для таких целей. Работать этот код начинает в реальном режиме, так что только 16 бит, причем (мы же о PC?) грузится он в память по адресу 0000:7С00 и начинает выполняться оттуда. Грузится он из MBR, так что если это что-то более-менее большое - то сразу должен сам считать и загрузить остальное в память. Считайте, что com-файл DOS, только никаких org 100h - что получено, то просто положено в очень конкретную :) память - и на диске, и в RAM, и управление передается первому байту. Соответственно, какое уж тут название программы... Название (файла) - это уже файловая система, а тут ею и не пахнет. Мой совет: виртуальная машина с виртуальным дисководом для дискет :), грузящаяся с такой виртуальной дискеты. Первый сектор - ваш. Это позволит вам делать, что хотите, ничем не рискуя, и очень легко записывать ваше творение в файл, не мучаясь с записью в конкретные сектора на диске/флешке (при этом можно легко натворить неприятностей).

Ответ 2



Да. Записать её в MBR флешки, а в BIOS выбрать загрузку с USB приоритетнее, чем с винчестера. Проще всего получить ассемблерный листинг и ввести код с помощью HexEdit прямо в MBR диска. Старайтесь в названии не использовать чужие торговые марки. Стоит. Отличный ассемблер.

Ответ 3



Если хотите поизучать низкоуровневое программирование - начните с микроконтроллеров там все под это дело заточено. Рекомендую с avr начать - есть много хороших статей по asm и с - очень удобные среды разработки и эмуляторы. Это если Вам x86 не принципиален.

Ответ 4



Да и практической пользы изучив низкоуровневое программирование МК все же поболее будет. Мало чем отличаться будет программирование x86 или МК если писать на C (С++ не очень распространен среди мк) с точки зрения обучения.Если конечно у Вас не стоит каких-то экзотических задач писать для 86 платформы.

Java. Конструктор, который использует сеттер - когда это нужно?

#java #конструктор #setter


Встретил такую конструкцию в обучающем примере. 

public class Vehicle {
  private String color;

 //Constructor
  Vehicle(String c) {
    this.setColor(c);
  }

  // Setter
  public void setColor(String c) {
    this.color = c;
  }
}


Насколько я понимаю, сеттер используют чтобы задать значение приватной переменной,
на что способен и конструктор. Зачем использовать конструктор, который использует сеттер,
который присвоит значение переменной, если можно обойтись конструктором? Бывают случаи
когда это необходимо, или это просто теоритический пример что так можно?
    


Ответы

Ответ 1



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

Ответ 2



Плохой обучающий пример. Я бы не рекомендовал использовать сеттеры внутри конструктора, особенно если сеттер простой. Если сеттер содержит много всего, то в этом случае лучше пересмотреть структуру класса, быть может, будет лучше использовать Билдер паттерн для создания экземпляра класса. Главная проблема с сеттерами внутри конструктора (а точнее, с сеттерами, которые могут быть переопределены) это то, что они могут создать кучу проблем, если метод будет переопределен. Цитата из книги JAVA:Эффективное программирование. 2е издание, Джошуа Блох. Статья 17. Конструкторы класса не должны вызывать переопределяемые методы, непосредственно или опосредованно. Нарушение этого правила может привести к аварийному завершению программы. Конструктор суперкласса выполняется прежде конструктора подкласса, а потому переопределяющий метод в подклассе будет вызываться перед запуском конструктора этого подкласса. И если переопределенный метод зависит от инициализации, которую осуществляет конструктор подкласса, то этот метод будет работать совсем не так, как ожидалось

Почему буквы алфавита с “р” по “ю” не входят в диапазон “а-я”?

#python #регулярные_выражения #кодировка #python_2x #unicode


Поясню сразу: я знаю, что python 2 требует явного объявления строк как юникодовых.
Понимаю, что это и не должно работать корректно. Мне интересна "анатомия" поломки.
Что именно внутри re.compile() и regex.search() производит такой результат?



Судя по нижеприведённому коду, диапазон 'а-яё' не включает в себя диапазон 'р-ю',
зато диапазон 'р-ю' включает 'ё'. 

mcve.py:

# coding=utf-8

import re

# Это панграмма, она содержит все буквы алфавита
test = 'широкая электрификация южных губерний даст мощный толчок подъёму сельского
хозяйства'

regex1 = re.compile('[а-яА-ЯёЁ\s]+')
regex2 = re.compile('[а-яА-ЯёЁшьрэтфцюыхущчъ\s]+')
regex3 = re.compile('[а-яёшьрэтфцюыхущчъс\s]+')
regex4 = re.compile('[а-яр-ю\s]+')

print regex1.search(test).group()
print regex2.search(test).group()
print regex3.search(test).group()
print regex4.search(test).group()


Результат:


  �
  широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства
  широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства
  широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства  


Я убедился, что все буквы алфавита от "А" до "Я" и от "а" до "я" идут в Юникоде подряд,
кроме "Ёё", которые явным образом добавлены в регулярное выражение. 

Постепенно добавляя буквы, на которых прерывается поиск по первому выражению, я пришёл
к диапазону а-яА-ЯёЁшьрэтфцюыхущчъ. Если отсортировать добавленные буквы, получается
почти сплошной интервал: "ртуфхцчшщъыьэю".

Если убрать заглавные буквы, т.е. "[А-ЯЁ]", то неожиданным образом поиск прерывается
на "с". Интервал становится сплошным: с "р" и до "ю". Это regex3.

И наконец оказывается, что теперь интервал можно свернуть и даже убрать "ё" (regex4). 

Что вообще происходит?

 python --version
Python 2.7.6




Если явным образом сделать юникодовую строку и регулярное выражение, то всё работает
как должно. Но ведь как-то работает и без этого. Объясните, как?

test2 = u'широкая электрификация южных губерний даст мощный толчок подъёму сельского
хозяйства'
regex5 = re.compile(u'[а-яА-ЯёЁ\s]+')

    


Ответы

Ответ 1



Самый простой способ посмотреть, что же не так с регуляркой - поставить debug флаг. Причем в таком случае абсолютно нет нужды лезть в потроха - они покажут то же самое. В этом можно убедиться, если залезть руками в %python_folder%/Lib/sre_parse.py и добавить там пару print-ов на строку 438 - выглядит она как-то тактак: elif this == "[": # character set set = [] setappend = set.append ## if sourcematch(":"): ## pass # handle character classes if sourcematch("^"): setappend((NEGATE, None)) # check remaining characters start = set[:] while 1: this = sourceget() if len(this) == 1: # Вот ровно в этом месте парсер перебирает содержимое [] print(source.tell(), "ORD: ", ord(this)) if this == "]" and set != start: break Так вот этот print покажет все абсолютно то же самое, что и debug - то что буковки на самом деле не буковки. regex1 = re.compile('[а-яА-ЯёЁ\s]+', re.DEBUG) max_repeat 1 2147483647 in literal 208 range (176, 209) literal 143 literal 208 range (144, 208) literal 175 literal 209 literal 145 literal 208 literal 129 category category_space Сразу видно, что что-то совсем нечисто, потому что первым должен быть range(ord('a')-ord('я'), а вместо него чепуха какая-то. А все от того, что строки в кодировке UTF8 (указана явно в файле), а тип у них - байты. Они могут отображаться нормально в терминале, если кодировки совпадают. Я пользуюсь Pycharm и его консоль в UTF8. Но если бы я запустил то же самое на стандартном терминале винды, то отобразилась бы натурально каша (типа такой - ╨Я╤А╨╕╨▓╨╡╤В), ибо кодировка CP866. Например, print("Привет") # НО! for char in "Привет": print(ord(char), repr(char)) В debug выводе число 208 - это первая половинка UTF8 символа 'а' - 176 - вторая половинка, затем следует дефис и снова первая половинка символа 'я' - в этом можно убедиться, открыв исходник в любом HEX редакторе. В итоге неверный интервал. Соответственно, когда парсер перебирает содержимое квадратных скобок он натыкается не на буквы, а на байты, а точнее на половинки букв в кодировке UTF8. Можно симулировать поведение регулярки таким кодом: reg_min = 144 reg_max = 209 result = [] for char in test: ordedr = ord(char) if ordedr >= reg_min and ordedr <= reg_max: result.append(char) else: break print(ord(regex1.search(test).group())) print(list(map(ord, result))) >>> 209 >>> [209]

Ответ 2



Нативная поддержка Unicode в Python - только с третьей версии. Используйте ее. Обратите еще внимание на первые две строчки исправленного файла #!/usr/bin/python3 # -*- coding: utf-8 -*- import re # Это панграмма, она содержит все буквы алфавита test = 'широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства' regex1 = re.compile('[а-яА-ЯёЁ\s]+') regex2 = re.compile('[а-яА-ЯёЁшьрэтфцюыхущчъ\s]+') regex3 = re.compile('[а-яёшьрэтфцюыхущчъс\s]+') regex4 = re.compile('[а-яр-ю\s]+') print(regex1.search(test).group()) print(regex2.search(test).group()) print(regex3.search(test).group()) print(regex4.search(test).group()) Вывод: gaal@linux-t420:~/WORK/test> ./1.py широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства широкая электрификация южных губерний даст мощный толчок подъ

Есть ли в .NET какой-нибудь ConnectionDialog?

#c_sharp #sql #net #sql_server #adonet


Если в .NET какой-нибудь ConnectionDialog, где пользователь может выбирать сервера
с БД и каталоги?

Нашел какой-то класс- DataLinks, но вроде он COM'овский, а так же на NUGET какую-то
библиотеку 2010 года.

Есть чего-нибудь из коробки или более свежее?
    


Ответы

Ответ 1



Из коробки (почти) существует решение под названием Microsoft Data Connection Dialog: О том, как правильно его готовить - есть статья на Хабре. Но она старая, и не очень актуальная в связи с не очень работающими ссылками. Тем не менее, в MSDN есть описание данного класса, а так же фабрика для создания таких окон. Так же на английском SO есть вопрос (с ответом) про этот диалог. Еще оно живет в NuGet, но более ранней версии — датировано 2011 годом. Какую именно версию использовать (и использовать ли) — решать вам. Более того, есть более свежие исходные коды данного проекта, которые живут теперь на GitHub (на момент написания данного ответа последние изменения там — март 2015 г). Судя по названию файлов с исходниками, данный диалог поддерживает: подключение к разным версиям MS SQL Server; подключение к Oracle; подключения к другим источникам через ODBC и OLE DB. IMHO: Лично я бы использовал версию с Гитхаба.

Ответ 2



Интерфейс OleDB это поддерживает, где-то видел пример на с++. На с# это можно через ADO так: using System.Reflection; using System.Runtime.InteropServices; public string GetConnectionString(string str) { string connectionString=string.Empty; // возвращаемая строка подключения object cn=null, // ADODB.Connection ud=null; // MSDASC.DataLinks try { Type AdoDbConnectionType=Type.GetTypeFromProgID("ADODB.Connection"); Type MSDascDataLinksType=Type.GetTypeFromCLSID(new Guid("{2206CDB2-19C1-11D1-89E0-00C04FD7A829}")); // создать COM объекты ADODB.Connection и MSDASC.DataLinks cn=AdoDbConnectionType.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[0]); ud=MSDascDataLinksType.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[0]); AdoDbConnectionType.InvokeMember("ConnectionString", BindingFlags.SetProperty, null, cn, new object[]{str}); if((bool)(MSDascDataLinksType.InvokeMember("PromptEdit", BindingFlags.InvokeMethod, null, ud, new object[]{cn}))) { // нажали кнопку ОК: connectionString=(string)(AdoDbConnectionType.InvokeMember("ConnectionString", BindingFlags.GetProperty, null, cn, new object[0])); } } catch{ /* обработка ошибок */ } finally { if(cn!=null) Marshal.ReleaseComObject(cn); cn=null; if(ud!=null) Marshal.ReleaseComObject(ud); ud=null; } // вернуть строку подключения return(connectionString); } Источник http://www.sql.ru/forum/120264/ne-zauchivayte-stroku-konekta#952795 В str можно заносить предустановленые свойства для линка, плюс в Provider записать имя провайдера (формате ODBC например MSDASQL алиас это MSSQL ).

Сделать глубоковложенный список плоским без ветвления и циклов

#python #алгоритм #python_3x #list


Доброго времени суток всем.

Есть список:

lst = [1, [2, 3], 4, [[6, 7]]]


Его нужно привести к такому виду:

lst = [1, 2, 3, 4, 6, 7]


При этом нельзя использовать ветвления и циклы, только стандартные методы. Задачка
простая, но взорвала мой мозг, может более опытные товарищи помогут?

Использую Python 3.5
    


Ответы

Ответ 1



just for fun =) flatten = lambda lst :eval('['+str(lst).replace('[','').replace(']','')+']') flat_list = flatten(lst) Но лучше не использовать eval() в вашем коде, подробнее здесь Ну или так (можно указать несколько типов): flatten = lambda lst: isinstance(lst, (int, str, float, bool)) and [lst] or sum(map(flatten, lst), []) lst = ["a", [2.3, True], 'abc', [[6, 7]]] flat_list = flatten(lst) print(flat_list) ['a', 2.3, True, 'abc', 6, 7]

Ответ 2



Для начала, вот версия, которая использует ветвления и циклы, чтобы было ясно ожидаемое поведение: from collections import Iterable def flatten_gen(nested, isatom=lambda x: not isinstance(x, Iterable)): for item in nested: if isatom(item): yield item else: yield from flatten_gen(item) Пример: lst = [1, [2, 3], 4, [[6, 7]]] print(list(flatten_gen(lst))) # -> [1, 2, 3, 4, 6, 7] где isatom() предикат определяет что является атомом (неразрывным объектом) с точки зрения этого метода. Подразумевается, что "не атомы" можно в цикл передать, чтобы получить объекты, из которых они состоят. Алгоритм прямолинейный: перебирая элементы переданного списка, отдаём атомы как есть, а для составных объектов, вызываем генератор рекурсивно. Так как строки по умолчанию работают с циклами, то если вы хотите их рассматривать как атомы в вашем случае, то передайте isatom явно: isatom = lambda x: isinstance(x, str) or not instance(x, Iterable) Если только целые числа считаются атомами, то isatom = lambda x: isinstance(x, int). Рекурсивная версия без явного ветвления, циклов, используя iterable unpacking синтакс: def flatten(nested): try: first, *rest = nested except TypeError: # not an iterable return [nested] except ValueError: # empty return [] return flatten(first) + flatten(rest) Код отпиливает первый элемент от заданного вложенного списка и соединяет плоский список, полученный из него с помощью рекурсивного вызова flatten(first), с плоским списком, полученным вызовом с остатком входного списка flatten(rest). Если не получается отпилить первый элемент из-за того, что ввод пустой или скаляр дан (не перечислимое значение—число к примеру), то возвращается пустой список или этот скаляр внутри списка соответственно. Если на входе вложенный список с числами, то на каждом уровне задача уменьшается до тех пор пока функция не будет вызвана с простейшими параметрами: число или пустой список, что приводит к возвращению конкретных значений и собиранию конечного результата при подъёме по стеку вызовов. Данная реализация не работает со строками без адаптации, так как 'a' == 'a'[0] (строка из одного символа равна своему первому символу)—это приводит к бесконечной рекурсии в данном случае. Код не оптимален по производительности (для простоты), но нет if/else ветвлений и циклов. Вот похожая, но более эффективная версия, которая использует генератор: def flatten_gen(nested): try: it = iter(nested) except TypeError: # not an iterable yield nested return try: first = next(it) except StopIteration: # empty return yield from flatten_gen(first) yield from flatten_gen(it) Генератор пытается получить итератор it с помощью iter(nested) и если на входе скаляр (например, целое число), то выбрасывается TypeError и генератор возвращает этот скаляр (yield). next(it) возвращает первый элемент из итератора, если он не пустой. Затем рекурсивно генерируются скаляры для первого элемента и оставшихся элементов в it итераторе. Если можно использовать циклы из библиотечного кода, наподобие sum(map(flatten, lst, [])) решения из @borisrozumnuk ответа, то можно улучшить flatten_gen() генератор, используя itertools.chain(): from itertools import chain def flatten_gen(nested): try: it = iter(nested) except TypeError: # not an iterable yield nested else: yield from chain.from_iterable(map(flatten_gen, it)) Так как рекурсия не используется в этом случае, чтобы цикл реализовать, то количество вызовов вдвое меньше. Если хочется, можно в виде одного выражения записать: def flatten(nested): return (isinstance(nested, int) and [nested] # an int or nested # empty and flatten(nested[0]) + flatten(nested[1:])) # non-empty list Что работает, благодаря short-circuit поведению and/or логических операторов. Не ясно рассматривать ли это как использование ветвления. Если nested это целое, то код возвращает это число, обёрнутое в список ([nested]). Если nested не целое число (а список), то он сразу возвращается, если он пустой (len(nested) == 0 означает, что bool(nested) is False). Если nested не пустой список, то возвращается объединение плоских списков, возвращаемое рекурсивными вызовами для первого элемента и остатка от списка—также как и в решениях выше в этом ответе. Если двигаться дальше в сторону нечитаемости кода, то ветвление можно заменить с помощью индексирования списков и lambda, чтобы задержать выполнение: def flatten(nested): return [ lambda: [ # list (not an int) lambda: flatten(nested[0]) + flatten(nested[1:]), # non-empty list lambda: [] # empty ][not nested](), lambda: [nested] # an int ][isinstance(nested, int)]() Общая идея: True == 1 and False == 0 в Питоне, поэтому выражение [on_false, on_true][condition]() вызывает on_false функцию, если condition ложно и on_true—если истинно.

Ответ 3



Вам необходим рекурсивный обход элементов списка. К примеру: lst = [1, [2, 3], 4, [[6, 7]]] def lst_to_flat(S): if S == []: return S if isinstance(S[0], list): return lst_to_flat(S[0]) + lst_to_flat(S[1:]) return S[:1] + lst_to_flat(S[1:]) print(lst_to_flat(lst))

Ответ 4



lst = [1, [2, 3], 4, [[6, 7]]] q = str(lst) a = q.replace("[",'') b = a.replace("]",'') g = b.replace(",",'') y = g.replace(" ",'') s = list(map(int, list( y ))) print (s) Посоветовал старший товарищ. Этот код не будет работать, если в изначальном массиве будут строки, числа и булевые значения, но для конкретного примера он сработает. Ответы senior-zero и jfs - лучшие (ИМХО).