Страницы

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

пятница, 7 декабря 2018 г.

Вызов конструктора класса родителя в Java. В чём важность?

Доброго времени суток всем. Читая книгу Брюса Эккеля, натолкнулся на следующий текст:
Конечно, очень важно, чтобы подобъект базового класса был правильно инициализирован, и гарантировать это можно только одним способом: выполнить инициализацию в конструкторе, вызывая при этом конструктор базового класса, у которого есть необходимые знания и привилегии для проведения инициализации базового класса. Java автоматически вставляет вызовы конструктора базового класса в конструктор производного класса.
Помогите разобраться, почему же так важно вызвать конструктор базового класса для класса наследника? И что будет если это не произойдёт (теоретически)? Спасибо.


Ответ

Базовый класс по сути является как-бы частью наследника. Поэтому, если он не будет проинициализирован, то работать ничего не будет.
Пример:
public class A { protected int valA = 1; }
public class B extends A { protected int valB = 2; public int getVal(){ return valB + valA; } }
Наследование позволяет писать такую конструкцию. Если родительский класс не будет инициализировн, то в строке return valB + valA; будет неизвестно значение valA.
Насколько я понимаю, не может быть ситуации, когда конструктор базового класса не вызовется (поправьте меня, если я ошибаюсь). Пример автоматического вызова конструкторов базовых классов

Зачем нужен метод Arrays.asList()?

Зачем нужен метод Arrays.asList() ? В чем его суть?


Ответ

Как и следует из документации: формирует список на основе массива. Массив при этом используется для внутреннего представления списка. Таким образом сохраняется связь между списком и исходным массивом:
изменения в массиве отразятся на списке:
String[] a = { "foo", "bar", "baz"}; List list = Arrays.asList(a); System.out.println(list); // [foo, bar, baz]
a[0] = "aaa"; System.out.println(list); // [aaa, bar, baz] изменения в списке отразятся на массиве:
String[] a = { "foo", "bar", "baz"}; List list = Arrays.asList(a); System.out.println(list); // [foo, bar, baz]
list.set(0, "bbb"); System.out.println(Arrays.toString(a)); // [bbb, bar, baz]
Если массив содержит объекты, очевидно, и массив и список будут ссылаться на одни и те же экземпляры:
Object[] a = { new Object(), new Object(), new Object()}; List list = Arrays.asList(a); System.out.println(a[0] == list.get(0)); // true

Как уже упомянул @Oleg Chiruhin, тот факт, что метод принимает в качестве параметра массив в форме аргументов переменной длины, позволяет использовать его для относительно удобной инициализации списков:
List list = Arrays.asList(new Foo(...), new Foo(...), new Foo(...));
сравните с
List list1 = new ArrayList<>(); list.add(new Foo(...)); list.add(new Foo(...)); list.add(new Foo(...));

Как пользоваться делегатами?


Я новичёк в этом деле, пытаюсь изучить и научиться пользоваться делегатами. Подскажите, как исправить?


Ответ

Смотрите.
Делегат — это просто такой тип, что в переменные этого типа вы можете записывать не числа и объекты, а функции*, и вызывать их потом. Вот собственно и всё, что вам нужно знать для начала.
Чтобы объявить такую переменную, компилятор должен знать какого типа аргументы будут у этой функции, и какого типа возвращаемое значение. Это ему нужно, чтобы он знал, как правильно вызывать функцию по этой переменной, и какого типа будет результат. Когда вы объявляете делегат, вы именно эту информацию и даёте:
delegate int mydel(int x, int y); ^ \_____________________/ | | объявляется делегат | | а это показывает, какие функции использовать с этим делегатом только вместо имени функции название делегата
У вас в коде функция возвращает значение, поэтому в определении делегата тоже должно быть возвращаемое значение.
Вы можете исправить код так:
class Program { delegate int mydel(int x, int y); // поменяли возвращаемое значение на int
static int sum(int x, int y) { return x + y; }
static void Main(string[] args) { mydel g = sum; // здесь можно без new, просто присвоить. так проще, правда? int result = g(10, 5); Console.WriteLine("Sum = " + result); } }

*(Примечание для знатоков.) Да, это упрощение. На самом деле у нас multicast delegate, так что их можно складывать и вызывать через Invoke/BeginInvoke. И да, делегат является объектом. Я упрощаю картину для понятности.

Случаи указания типа данных в условии цикла

Добрый вечер. Пришел с вопросом. Читал тут книгу, в одном фрагменте было написано, что если я укажу тип данных у переменной в условии функции, то значение переменной будет иметь большую область видимости, чем если бы я указал переменную без типа. То есть без типа переменная будет действовать, пока активен цикл, но с типом данных она будет действовать и на еще одни скобки.
Решил проверить. Написал с int'ом-все сработало. Убрал int-и программа стала использовать значение переменной в цикле во всей программе и по какому-то принципу компилятор его даже изменил, по какому-я не понимаю.
Вопрос: Я некорректно понял инфу из учебника? Да, то как все должно быть на самом деле?
#include using namespace std;
int main() { int done=5; int howmany=3;
for( done=1; done cout<Вывод:
7 7 3


Ответ

Рассмотрим следующую программу
#include
int main() { int done = 5; int howmany = 3;
for ( done = 1; done < howmany; done++ ) { std::cout << done << std::endl; }
std::cout << done << std::endl;
return 0; }
Ее вывод на консоль
1 2 3
В этой программе в блоке кода функции main объявлена переменная done. Сначала эта переменная инициализируется значением 5
int done = 5;
а затем в предложении for ей присваивается значение 1
for ( done = 1; done < howmany; done++ ) ^^^^^^^^
После выхода из цикла эта переменная будет иметь значение 3.
Теперь рассмотрим следующую программу
#include
int main() { int done = 5; int howmany = 3;
for ( int done = 1; done < howmany; done++ ) { std::cout << done << std::endl; }
std::cout << done << std::endl;
return 0; }
Ее вывод на консоль
1 2 5
В этой программе в предложении for объявляется переменная с именем done, совпадающем с именем переменной, объявленной в блоке кода main.
for ( int done = 1; done < howmany; done++ ) ^^^^^^^^^^^^
Эта переменная в предложении for скрывает одноименную переменную с таким же именем, объявленную во внешнем блоке кода. Ее область определения - это тело цикла for. После выхода из цикла данная переменная прекращает свое существование.
Из стандарта C++ (6.5.3 The for statement)
3 If the for-init-statement is a declaration, the scope of the name(s) declared extends to the end of the for statement.
Поэтому последняя строка вывода программы
5
уже выводит на консоль значение переменной done, которая была объявлена до цикла в блоке кода функции main
В общем случае имя, объявленное во внутренней области объявления скрывает такое же имя, объявленное во внешней области объявления
Из стандарта C++ (3.3.10 Name hiding)
1 A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).
Обратите внимание, что есть важное отличие в определении предложения for между C++ и C.
В C++ во второй части предложения for, где записывается условие, также может быть объявление.
Рассмотрите следующую демонстрационную программу
#include
int main() { int done = 5; int howmany = 3;
for ( ; int howmany = done; done-- ) { std::cout << done << std::endl; }
std::cout << '
' << howmany << std::endl;
return 0; }
Ее вывод на консоль
5 4 3 2 1
3

При каждой итерации данного цикла for
for ( ; int howmany = done; done-- ) ^^^^^^^^^^^^^^^^^^
объявляется переменная howmany, которая скрывает переменную с таким же именем, объявленную в функции main. Значение, присвоенное этой переменной, преобразуется к булевскому типу. Если оно равно 0, то условие будет ложным и произойдет выход из цикла.
После цикла выводится уже значение переменной howmany, объявленной в main
Ну, и напоследок пример, когда в предложении for сразу в двух его частях объявляются переменные, которые скрывают одноименные переменные с теми же именами, объявленные в main. После выхода из цикла эти переменные прекращают свое существование, и становятся видимыми переменные, объявленные в main
#include
int main() { int done = 10; int howmany = 20;
for ( int done = 5; int howmany = done; done-- ) { std::cout << done << std::endl; }
std::cout << '
' << done << std::endl; std::cout << howmany << std::endl;
return 0; }
Вывод этой программы на консоль
5 4 3 2 1
10 20

Предложение for в этой программе логически может быть представлено как
{ int done = 5;
Label_repeat: { int howmany = done;
if ( howmany != 0 ) { std::cout << done << std::endl; done--; goto Label_repeat; } else { goto Label_exit; } } } Label_exit: //...

WPF Таблица XAML

Ребят подскажите мне тугому как сделать подобную выгрузку элементов с бд с постраничной или динамической прокруткой данных? Задача: В базе хранятся фотографии и фио студентов. Требуется выводить фотографию и под ней ФИО студента.

Прошу прощения за столь глупый вопрос но для меня это пока очень сложно. Возможно кто подскажет готовый пример. Это было бы проще для меня
На данный момент инф. берется таким способом.


Ответ

Окей, ну давайте напишем как это нужно делать. Попробуем сразу сделать из этого MVVM.
Начнём с модели. Главное — модель должна стараться абстрагировать от нас то, что у неё под капотом. Имея это в виду, организуем её просто как список модельных объектов в памяти. Внешний код не должен от этого зависеть.
Итак:
public class StudentModel { public string Name { get; set; } public Uri ImageUri { get; set; } }
public class StudentListModel { const string uri = "https://www.gravatar.com/avatar/f81585f940e68c3bab934cc6f59c901e?" + "s=328&d=identicon&r=PG";
public IEnumerable GetAllStudents() => Enumerable.Range(0, 200).Select(n => new StudentModel() { Name = $"Student #{n}", ImageUri = new Uri(uri) }); }
Функциональность «получить n-ую страницу» я оставляю на VM, держа в голове, что в реальности у вас вместо IEnumerable будет IQueryable. Если вам хочется сделать репозиторий поверх вашего обращения с базой (например потому, что вы не пользуетесь EF), почему бы и нет.
С моделью покамест всё, впиливать базу сюда уж придётся вам лично. Перейдём к VM.
Для начала, базовый класс для VM. Если вы пользуетесь каким-нибудь MVVM-фреймворком, он у вас уже есть. Если нет, напишем его. Он должен имплементировать INotifyPropertyChanged
class VM : INotifyPropertyChanged { protected bool Set(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false;
field = value; RaisePropertyChanged(propertyName); return true; }
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged; }
Теперь класс, отвечающий за одного студента. С этим всё просто:
class StudentVM : VM { string name; public string Name { get { return name; } set { Set(ref name, value); } }
Uri imageUri; public Uri ImageUri { get { return imageUri; } set { Set(ref imageUri, value); } }
public StudentVM(string name, Uri uri) { this.name = name; this.imageUri = uri; } }
Если вы пишете в соответствии с трендами, и у вас иммутабельные классы, получится проще:
class StudentVM : VM { public string Name { get; } public Uri ImageUri { get; }
public StudentVM(string name, Uri uri) { this.Name = name; this.ImageUri = uri; } }
Дальше — класс, занимающийся списком студентов. Будем писать его по кускам. Начнём.
class StudentListVM : VM {
Нам нужно свойство, отвечающее за количество страниц. Без него никак. Пишем:
int totalPages; public int TotalPages { get { return totalPages; } private set { if (value < 0) throw new ArgumentException(nameof(TotalPages)); Set(ref totalPages, value); } }
Пока всё просто. Да, наверняка мы захотим ещё показать в UI что-то особое, если у нас нет ни одного студента, поэтому положим вспомогательное свойство:
bool havePages; public bool HavePages { get { return havePages; } private set { Set(ref havePages, value); } }
и не забудем установить его в сеттере TotalPages
private set { if (value < 0) throw new ArgumentException(nameof(TotalPages)); Set(ref totalPages, value); HavePages = totalPages > 0; // <--- добавили }
Следующий пункт — номер текущей страницы. С ним всё то же самое, только проверок в сеттере побольше.
int currentPageNo; public int CurrentPageNo { get { return currentPageNo; } set { if (value < 0) throw new ArgumentException(nameof(CurrentPageNo)); if (value >= TotalPages && HavePages) throw new ArgumentException(nameof(CurrentPageNo)); if (value != 0 && !HavePages) throw new ArgumentException(nameof(CurrentPageNo)); Set(ref currentPageNo, value) } }
Теперь, когда текущая страница поменялась, мы должны вычитать новый список студентов. Это нужно сделать в фоне, не блокируя UI-поток. Добавляем в CurrentPageNo
set { if (value < 0) throw new ArgumentException(nameof(CurrentPageNo)); if (value >= TotalPages && HavePages) throw new ArgumentException(nameof(CurrentPageNo)); if (value != 0 && !HavePages) throw new ArgumentException(nameof(CurrentPageNo)); if (Set(ref currentPageNo, value)) // если изменения были... PopulateCurrentPage().IgnoreResult(); // IgnoreResult напишем позже }
Ещё нужно свойство, хранящее список студентов:
IEnumerable currentPage; public IEnumerable CurrentPage { get { return currentPage; } private set { Set(ref currentPage, value); } }
и код, занимающийся обновлением. Он сложный.
// токен для остановки бегущего обновления CancellationTokenSource populationTaskCts = null; async Task PopulateCurrentPage() { // если старое обновление ещё бежит, отменяем его populationTaskCts?.Cancel(); // начиная с этой точки, старое обновление нам не помешает CurrentPage = null; if (!HavePages) return; using (var cts = new CancellationTokenSource()) { // теперь мы - текущее обновление populationTaskCts = cts; var workPageNo = CurrentPageNo; // может поменяться в процессе
try { // асинхронно получаем модельный список var modelPage = await Task.Run(() => GetStudentsFromModel(workPageNo, cts.Token), cts.Token); if (cts.IsCancellationRequested) return; // создаём VM-объекты в UI-потоке var vmPage = modelPage.Select(p => new StudentVM(p.Name, p.ImageUri)) .ToList(); if (cts.IsCancellationRequested) return; // если мы оказались тут, то мы всё ещё текущее обновление Debug.Assert(workPageNo == CurrentPageNo); CurrentPage = vmPage; // и можем присвоить результат } catch (OperationCanceledException) when (cts.IsCancellationRequested) { // ничего не делаем, нас отменили } finally { // если мы текущее обновление, убираем за собой токен if (cts == populationTaskCts) populationTaskCts = null; // в противном случае там чужой токен, его не трогаем } } }
Нам ещё нужен экземпляр модели и процедура GetStudentsFromModel, которая будет их вычитывать:
StudentListModel model = new StudentListModel();
const int pageSize = 10; IEnumerable GetStudentsFromModel(int pageNo, CancellationToken ct) { // make sure it's materialized on background thread return model.GetAllStudents().Skip(pageNo * pageSize).Take(pageSize).ToList(); }
К ней ещё понадобится (позже) подсчёт количества страниц:
int GetPageCount(CancellationToken ct) { var studentCount = model.GetAllStudents().Count(); return (int)Math.Ceiling((double)studentCount / pageSize); }
Сейчас эти процедуры синхронные, но если ваша модель будет выставлять async-интерфейс (как это делает новый EF), их нужно будет сделать асинхронными.
Далее. Нам нужна команда изменения текущей страницы.
public ICommand RequestPageChange { get; }
И её имплементация: метод, который будет отрабатывать:
void OnPageChangeRequest(int newPage) { if (!HavePages) return; if (newPage < 0 || newPage >= TotalPages) return; // log an error? CurrentPageNo = newPage; }
Привязывать команду к имплементации придётся в конструкторе. Заодно и заведём его:
public StudentListVM() { RequestPageChange = new RelayCommand(OnPageChangeRequest); }
(класс RelayCommand есть практически везде, я приведу его позже).
Да, нам ещё в конструкторе нужно запустить подсчёт количества страниц и первоначальное заполнение:
public StudentListVM() { RequestPageChange = new RelayCommand(OnPageChangeRequest); StartInitialPopulate().IgnoreResult(); }
async Task StartInitialPopulate() { TotalPages = await Task.Run(() => GetPageCount(CancellationToken.None)); currentPageNo = 0; await PopulateCurrentPage(); } }
Кажется, ничего не забыли. Класс получился слишком большой, возможно, имеет смысл разрефакторить его на мелкие куски.
Ещё у нас тут упоминался RelayCommand
public class RelayCommand : ICommand { public RelayCommand(Action onExecute) { OnExecute = onExecute; }
public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) => true; public void Execute(object parameter) => OnExecute((T)parameter);
readonly Action OnExecute; }
и функция IgnoreResult
public static class TaskExtgensions { public static void IgnoreResult(this Task t) { } }
На этом с вью-моделью, кажется, всё, перейдём к представлению.
Вью-модель прикрепляем к представлению, как описано здесь
App.xaml:

App.xaml.cs:
using System.Windows;
using Pages.View; using Pages.VM;
namespace Pages { public partial class App : Application { StudentListVM mainVM = new StudentListVM();
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); new MainWindow() { DataContext = mainVM }.Show(); } } }
Теперь идём в MainWindow.xaml. Начинаем:

На нашем мок-апе есть явно две области: нижняя маленькая со списком страниц, и верхняя большая с содержимым текущей страницы. Это напрашивается на Grid

Теперь, верхний элемент. Он показывает список элементов, без текущего элемента и всего такого. Так что это ItemsControl

Теперь, как расположены элементы в нём? Они идут не в столбик, а «перетекают» по горизонтали в следующую строку. Отлично, значит, нам нужно установить в ItemsPanel подходящий контейнер:

Как выглядит отдельный элемент? Это не стандартный ToString(), это текст с картинкой. Напишем для этого ItemTemplate

Вроде бы ничего не забыли.

Окей, с таблицей справились, теперь нам нужен список страниц. Откуда его взять? У нас по идее есть только количество страниц, нужно его превратить в список при помощи конвертера. Затем, мы ведь не хотим показывать все страницы? Их может быть очень много. Будем показывать первые страницы, последние страницы и страницы вокруг текущей.
Засучим рукава и вперёд!
Для начала, тип для описания страницы или заполнителя:
enum PageEntryType { Normal, Current, Ellipsis }
struct PageEntry { public int PageNumber { get; } public PageEntryType Type { get; } public PageEntry(int num, PageEntryType type) { PageNumber = num; Type = type; } }
Ну и сам конвертер. Надеюсь, я не напутал с вычислениями.
Нам нужен IMultiValueConverter, потому что у нас два входных значения.
class PageListConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { int currentPage = (int)values[0]; int numberOfPages = (int)values[1]; return RecalcList(currentPage, numberOfPages); }
IEnumerable RecalcList(int currentPage, int numberOfPages) { const int pagesAroundCurrent = 3; // сколько страниц вокруг текущей const int pagesAroundEnd = 2; // сколько страниц по краям
var min = Math.Max(0, currentPage - pagesAroundCurrent); var max = Math.Min(numberOfPages - 1, currentPage + pagesAroundCurrent);
// перекрывается список вокруг текущей страницы со списком у левого края? bool separateLeftEnd = pagesAroundEnd + 1 < min; if (!separateLeftEnd) min = 0;
// перекрывается список вокруг текущей страницы со списком у правого края? bool separateRightEnd = numberOfPages - 1 - pagesAroundEnd - 1 > max; if (!separateRightEnd) max = numberOfPages - 1;
if (separateLeftEnd) { for (int n = 0; n < pagesAroundEnd; n++) yield return new PageEntry(n, PageEntryType.Normal); // между списками разрыв - многоточие yield return new PageEntry(-1, PageEntryType.Ellipsis); }
for (int n = min; n <= max; n++) yield return new PageEntry(n, (n == currentPage) ? PageEntryType.Current : PageEntryType.Normal);
if (separateRightEnd) { // между списками разрыв - многоточие yield return new PageEntry(-1, PageEntryType.Ellipsis); for (int n = numberOfPages - pagesAroundEnd; n < numberOfPages; n++) yield return new PageEntry(n, PageEntryType.Normal); } }
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }
Отлично, список у нас есть, теперь его нужно показать. Как показывать список? Возвращаемся к нашему недописанному XAML'у. Нам нужен ItemsControl, как обычно. Кладём его во вторую строку и добавим небольшой маргин.

В ресурсы положим конвертер

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

Окей, дальше нам нужно эти элементы расположить по горизонтали. Для этого сгодится StackPanel. Саму панель тоже центрируем:

Теперь отображение каждого элемента списка. У нас есть три варианта отображения: обыкновенная страница показывается в виде ссылки, текущая — без ссылки, но жирным шрифтом, а на месте для многоточия должно появиться многоточие. Выбор из этих трёх вариантов делаем при помощи привязки Visibility блока к значению нашего элемента через конвертер. Конвертер мы напишем позже, он будет делать видимым только один из трёх нужных элементов. Сам конвертер мы добавим в ресурсы ItemsControl'а:

Продолжаем:

Располагаем элементы один на другом, видимым будет только один.

Первый вариант: нормальное отображение. Нам нужна гиперссылка. По нажатию на неё вызовем обработчик из code-behind.

Второй вариант: текущая страница. Вместо ссылки — TextBlock с жирным шрифтом.

Ну и третий вариант — просто многоточие.

Вот и всё с отображением элемента.

Больше в XAML'е делать нечего.

Заимплементируем обработчик клика по ссылке. Он лежит в классе MainWindow
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); }
void OnPageChangeRequest(object sender, RoutedEventArgs e) {
Нам нужно где-то взять команду. Чтобы не лазить по DataContext'у, сделаем простой трюк: привяжем эту команду в свободное свойство. Например, в Tag. (Это мы сделаем потом, снова-таки в XAML'е.)
var command = (ICommand)Tag; // прочитали оттуда команду if (command == null) return;
var hyperlink = (Hyperlink)sender;
Номер страницы можно вытащить из DataContext'а гиперссылки.
var pageNo = ((PageEntry)hyperlink.DataContext).PageNumber; command.Execute(pageNo); } }
Возвращаемся в XAML и дописываем Tag

Последняя недописанная вещь — PageEntryTypeToVisibilityConverter. Он очень простой:
public class PageEntryTypeToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { PageEntryType expectedType = (PageEntryType)parameter; PageEntryType actualType = ((PageEntry)value).Type; return expectedType == actualType ? Visibility.Visible : Visibility.Collapsed; }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }
Запускаем и получаем:


Да, а давайте ещё сделаем так, чтобы при пустом списке нам показывалось какое-то разумное сообщение. Это просто. Возвращаемся в XAML и добавляем ещё один элемент во внешний Grid

Но так этот элемент будет виден всегда, а нам нужно только когда страниц нету. Не проблема, привяжемся к свойству HavePages. Нам нужно сконвертировать его в Visibility. Но стандартный BooleanToVisibilityConverter конвертирует не в ту сторону, в которую нам надо, поэтому украдём конвертер отсюда
public class BooleanConverter : IValueConverter { public BooleanConverter(T trueValue, T falseValue) { OnTrue = trueValue; OnFalse = falseValue; }
public T OnTrue { get; set; } public T OnFalse { get; set; }
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (!(value is bool)) return DependencyProperty.UnsetValue; return ((bool)value) ? OnTrue : OnFalse; }
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }
public class BooleanToVisibilityConverter : BooleanConverter { public BooleanToVisibilityConverter() : base(Visibility.Visible, Visibility.Collapsed) {} }
Теперь можно написать так:

Нужно только положить его в ресурсы окна:

Дальнейшее наведение красоты — на вас.

Можете объяснить суть invoke

Есть основной поток, в котором я создаю все Controls в winforms. И есть второй поток, который вызывается через срабатывание события. В результате этого события мне нужно изменить значение DataSource у DataGridView. В результате возникает ошибка, что доступ к контролу пытается получить не основной поток, в котором был создан контрол. Решением является использование методов Invoke\BeginInvoke. Но саму суть я не понимаю этих методов и как их реализовать в коде. В создаваемом втором потоке используется функция доступа к dgv
private void RefreshTables() { try { if(con.State == ConnectionState.Closed) { con.Open(); } sql = "select rowid, * from OpenPos"; adapOpenPos = new SQLiteDataAdapter(sql, con); dsOpenPos = new DataSet(); adapOpenPos.Fill(dsOpenPos); dataGridView1.DataSource = dsOpenPos.Tables[0]; dataGridView1.Columns[0].Visible = false; dataGridView1.Columns[15].Visible = false; dataGridView1.Update(); con.Close();
if (con.State == ConnectionState.Closed) { con.Open(); } sql = "select rowid, * from ClosePos"; adapClosePos = new SQLiteDataAdapter(sql, con); dsClosePos = new DataSet(); adapClosePos.Fill(dsClosePos); dataGridView2.DataSource = dsClosePos.Tables[0]; dataGridView2.Columns[0].Visible = false; dataGridView2.Columns[14].Visible = false; dataGridView2.Update(); con.Close(); } catch (Exception e) { MessageBox.Show(e.Message); } }


Ответ

Суть метода Invoke довольно проста - он принимает делегат и выполняет его в том потоке, в котором был создан элемент управления, у которого вызывается Invoke. Как вы могли заметить, если обращаться к контролам в WinForms не из того потока, в котором они были созданы, будет выброшено исключение. Соответственно, метод Invoke полезен в случаях, когда необходимо работать с контролом из других потоков. Метод BeginInvoke делает то же самое, но асинхронно.
Небольшой пример использования Invoke
private void ButtonInvoke_Click(object sender, EventArgs e) { var myThread = new Thread(ThreadFunction); myThread.Start(); //метод выполняется в другом потоке }
private void ThreadFunction() { Thread.Sleep(1000); Action action = () => listBox1.Items.Add("value"); // Свойство InvokeRequired указывает, нeжно ли обращаться к контролу с помощью Invoke if (InvokeRequired) Invoke(action); else action(); }
Стоит также отметить, что async/await, добавленные в C# 5, позволяют обойтись без Invoke
private async void ButtonAsync_Click(object sender, EventArgs e) { listBox1.Items.Add("first"); await Task.Run(async () => { await Task.Delay(1000); }); // этот код будет продолжен в UI потоке, // и здесь нет необходимости использовать Invoke listBox1.Items.Add("second"); }

Как можно оценить суровость выполненного запроса?

Например, есть некоторый сервис, который время от времени выполняет запросы и нужно определить насколько запрос был "тяжелым".
По каким критериям это можно оценить?
Первое, что пришло на ум- это замерить загрузку процессора на SQL Server до и во время выполнения запроса.
По каким критериям еще можно понять, что запрос оказался тяжелым и мог негавтивно сказаться на работу других пользователей с БД?
Собственно, это нужно для программы балансировки нагрузки, что бы выполнять тяжелые запросы в наиболее подходящее время. Собственно, не помешал бы минимальный пример, как эту статистику можно получить из когда программы.


Ответ

Тяжесть зависит от плана выполнения. План выполнения может меняться в зависимости от статистики по таблицам. Статистика может устаревать и порождать неверные планы. Так что "тяжесть" - величина непостоянная.
Кроме того, в SQL Server есть кэш данных, и запрос, выбирающий данные их кэша, ес-но будет выдавать меньше физических чтений с диска. Поэтому при замерах стоит смотреть на количество логических чтений, а не только на количество физических.
Грубо можно оценить, замерив время и IO напрямую:
SET STATISTICS TIME ON SET STATISTICS IO ON
запрос
Выполнить несколько раз, отбросить максимальное и минимальное значение по CPU, по остальным - взять среднее.
Показатели IO будут зависеть от плана, так что они при последовательных выполнениях не поменяются. Для воспроизведения промаха кэша - почистите его вызовом CHECKPOINT; DBCC DROPCLEANBUFFERS;
Из .NET значения статистики можно получить, выставлением свойства sqlConnection.StatisticsEnabled = true + вызовом sqlConnection.RetrieveStatistics()

Что делать с "плохим" запросом - достаточно обширный вопрос. Минимальный набор действий
пересчитать статистику для используемых таблиц(обязательно, иначе есть шанс бороть несуществующую проблему!) получить свежий план выполнения если план плохой - оптимизировать запрос, индексы или структуру базы
Что точно не стоит делать - это править SQL наугад. Оптимизация SQL - достаточно простой процесс, но при этом нужно точно представлять механизм выполнения запросов SQL Server, иначе можно получить кучу скрытых проблем :)

Что такое стек и куча? [дубликат]

На данный вопрос уже ответили: Представление кучи и стека 3 ответа Я изучаю C# и пытаюсь понять что такое стек и куча. В большинстве примеров, стек подается как массив у которого есть функции push() и pop(). Но это не достаточное объяснение для меня. Вот к примеру, у нас есть функция Calculate()
void Calculate() { int x; int y; object z; }
Какую роль в этом примере играет стек, а какую роль играет куча?


Ответ

Если рассматривать язык c# сам по себе, в отрыве от реализации, то действительно знать о том, что такое "стек" вам нет необходимости.
Но если вы хотите хорошо понимать тот инструмент, которым пользуетесь, то можно посмотреть немного под капот.
Платформа Microsoft .NET использует стековый байт-код Intermediate Language (IL, CIL, MSIL).
То есть код, который вы напишите, будет записан в опкодах этого языка и уже непосредственно его и будет выполнять CRL.
Посмотрим на какой-либо простой код c# и на то, как он записан опкодами:
void Main() { int a = 15; int b = 10; int c = a - b; Console.WriteLine(c); }
IL-код я получил в программе Linqpad, в IL DASM будет такой же:
IL_0000: nop IL_0001: ldc.i4.s 0F IL_0003: stloc.0 // a IL_0004: ldc.i4.s 0A IL_0006: stloc.1 // b IL_0007: ldloc.0 // a IL_0008: ldloc.1 // b IL_0009: sub IL_000A: stloc.2 // c IL_000B: ldloc.2 // c IL_000C: call System.Console.WriteLine IL_0011: nop IL_0012: ret
Что мы видим при наведении мыши на ldc или stloc? Мы видим всплывающие подсказки "pushes int value onto stack" или "pops value from top of stack".

То есть все ваши инструкции на c# оказываются очень тесно связанными со стеком. Даже простые операции типа сложения чисел проходят через стек. И циклы фактически тоже разворачиваются в стековые инструкции.
Это как раз устройство нижележащего этажа для c#. Современные языки программирования достаточно часто используют концепцию стековых машин, c# -- один из них.
PS Если хочется погрузиться в эту тему более основательно, то хорошей отправной точкой может стать статья Введение в MSIL [Кенни Керр, пер. Aquila]

Разбиение числа на цифры независимо от длины

Изучаю java и для практики решаю простенькие логические задачки, типа нахождения счастливого билетика. Часто встречаются задания, где на входе есть n-значное число, и необходимо что-то сделать с цифрами, из которого оно состоит (дан диапазон чисел от 1 до 999, найти, в скольких числах из диапазона встречается цифра 2). Все, что я знаю и умею - это разбивать ни цифры делением:
int a = 123 / 100 = 1 int b = 123 / 10 % 10 = 2 int c = 123 % 100
Остальная часть задачи проверяется условием. Собственно сам вопрос: есть ли в java какой либо метод, позволяющий разбивать число на цифры и не привязываться к его длине? Например, числа, в которых необходимо искать 2 и ммм 13! вводятся с клавиатуры? И длина числа ограничена только типом int.


Ответ

Что-то вы сильно мучаетесь с делением. Берите с конца. Взятие остатка от деления "%" числа на 10, всегда вам даст последнее из его цифр. И в цикл! int a = Integer.MAX_VALUE; StringBuilder s = new StringBuilder(); System.out.println(a); while(a>0) { s.insert(0, a%10); a = a / 10; } System.out.println(s.toString()); Причем навскидку, написать данный алгоритм можно не одним способом, выбирайте, кому как нравится: StringBuilder s = new StringBuilder(); for(int a=Integer.MAX_VALUE; a>0; a/=10) s.insert(0, a%10); System.out.println(s); Так же вам, и только вам, решать записывать цифры подряд, а в конце переворачивать строку, либо сразу писать все в начало строки.

Как будет hash (хеш) по-русски?

Для написания дипломных работ требуют использования только русских слов, а не заимствованных из английского языка. Например нельзя использовать: брандмауэр, логин, браузер. На английском можно только название программ, ОС и т.д. Друзья, есть ли у Вас идеи, как называют хеш (в моем случае хеш пароля) в научной литературе на русском? Я встречал - "дайджест сообщения", но "дайджест" тоже английское слово "digest". Видел перевод "сводка сообщения" в википедии, но никаких материалов, где такое словосочетание используется нет.


Ответ

В ГОСТ Р 34.11-94, начиная прямо с названия, используется слово «хэш» и его производные («хэширование»). Так что с этим словом все в порядке. За исключением вопроса правописания («хэш» или «хеш»). В ГОСТах тут слегка разброд и шатания, два ГОСТа одного года (34.10-94 и 34.11-94; да, первый неактуален, см. далее) использовали разные варианты («хеш» и «хэш» соотв.), в ГОСТ 34.10-2001 уже «хэш.» Так что, скорее всего, через «э», но тут я не уверен. Это, лучше, спросите на gramota.ru :)

Округление до N знаков после запятой в с++

есть число типа double, например 4.64452675
Как в С++ сделать округление до N знаков после запятой? Либо отсечение? Не нашел нужной функции


Ответ

static const double powerOfTen[] = { 1.0, 10.0, 100.0, ... };
double truncated = std::trunc(d * powerOfTen[N]) / powerOfTen[N]; double rounded = std::floor(d * powerOfTen[N] + 0.5) / powerOfTen[N];

Оператор delete: удаление массива

Здравствуйте. :) Вот такой вопрос возник, почему вот этот после выполнения int *x = new int[2]; delete x; идентичен вот этому int *x = new int[2]; delete []x; Везде где читал, сказано что массив нужно удалять вторым способом.


Ответ

Если вы выделяли память при помощи new[], вы обязаны освободить её с помощью delete[]. Так гласит стандарт.
Освобождение такой памяти как-то по-другому (delete без [] или вообще free) является Undefined Behaviour. Если в программе есть Undefined Behaviour, она имеет право вести себя как угодно: может ничего плохого не делать, может вылететь в любой точке, отформатировать ваш винчестер или выбросить из окна вашего кота.
You have been warned.

Удаление пустых элементов списка python

Извините за глупый вопрос, но как удалить пустые элементы из списка в пайтоне?


Ответ

Для начала стоит формализовать, что такое "пустой" элемент.
"Пустыми" можно считать многие элементы, например,
[], (), {}, set(), ''
в том числе
None, 0, 0.0, 0j, False
Если требуется выбросить из коллекции все такие элементы, то можно воспользоваться генератором списка
[value for value in values if value]
Или, если список не требуется, генераторным выражением:
(value for value in values if value)
Если правило определения "пустого" элемента более сложное, достаточно описать это правило после оператора if в указанных выше выражениях, например,
[value for value in values if value != [None]]
оставит в списке элементы, которыне не равны списку, содержащему None (важно заметить, что такой список будет пропущен в итоговый набор в первых случаях).

Как обработать исключение java.util.ConcurrentModificationException?

Есть код: ссылка на github. В ходе выполнения выбрасывается исключение Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException Знающие люди подскажите как побороть проблему. Заранее спасибо)


Ответ

Его не надо обрабатывать. Это исключение значит, что вы в своем коде удаляете элемент напрямую из коллекции во время использования итератора. Т.к. в этом случае появляется неопределенность, откуда итератор должен продолжать и какие последующие элементы должен пройти, выбрасываетс исключение. Для корректного изменения сущности используйте методы итератора, например, iterator.remove()

Найти 2 элемента массива, сумма которых равна заданному числу

Задачка кажется очень простой, но все равно не выходит.
Дан массив целых чисел, упорядоченный строго по возрастанию.
Дано некоторое число X, нужно менее чем за квадратное количество операций(то есть перебор всех пар) найти такие два любых элемента массива, что их сумма равна X, иначе вывести 0.
Как сделать это меньше, чем за квадратное время?
Подкиньте идею какую-нибудь, ну или сам подход?)) Спасибо=)


Ответ

Решение за О(n): Решаем методом "2 указателя" Храним 2 индекса: первый сначала указывает на нулевой элемент, второй - на последний элемент. Цикл - пока индексы не будут равны (то есть пока они не укажут на один и тот же элемент. Если такое случилось - значит, искомых элементов в массиве нет). На каждой итерации цикла сравниваем текущую сумму (сумму элементов, на которые указывают индексы) с искомой. Если сумма меньше - увеличиваем первый индекс, если сумма больше - уменьшаем второй индекс. Если равна - решение найдено.
#include #include
using namespace std;
int main() { int *a; // массив int n; // количество элементов в массиве int sum; // необходимая сумма /// //чтение массива cin >> n; a = new int[n]; for (int i = 0; i < n; i++) cin >> a[i]; cin >> sum; /// // индексы int lt = 0; // первый, то есть левый int rt = n - 1; // второй, то есть правый while (lt != rt) { int cursum = a[lt] + a[rt]; if (cursum < sum) lt++; else if (cursum > sum) rt--; else // if (cursum == sum) { cout << "indexes: " << lt << " " << rt << endl; cout << "values: " << a[lt] << " " << a[rt] << endl; return 0; } } cout << "not found" << endl; return 0; }

Вопрос про абстракцию и полиморфизм в java

Загвоздка такова: по отдельности я более-менее понимаю, о чем эти 2 парадигмы:
Абстрактный класс с методами - своего рода шаблон,который наследуют другие классы. Объект такого класса нет смысла создавать, т.к. он абстрактен, но наследники могут использовать его методы и переопределять их. Полиморфизм - это возможность использовать метод с одним именем в разных классах, но по-разному его реализовывать, переопределяя. Также понимаю,что в принципе одну систему можно построить как на базе абстрактного класса,так и используя полиморфизм.
Но что использовать лучше и чем они отличаются - я не понимаю, не хватает совсем немного,чтобы додумать. Поясните, пожалуйста, что и как, если можно на примерах. Заранее большое спасибо.


Ответ

Это тесносвязанные базовые понятия ООП, они дополняют друг друга. Поэтому нельзя говорить "что использовать лучше". Как вы написали,
абстрактный класс с методами своего рода шаблон,который наследуют другие классы
Так вот, вы задаете определенный шаблон для дальнейшей реализации полиморфизма. Когда от этого абстрактного класса у вас будут 2 или более наследника, реализующие абстрактные методы. Это и будет полиморфизм. Также, часто помогает осознать что это сама расшифровка слова. "поли" - означает много, "морфа" - форма. Полиморфизм - много форм. Есть одна абстракция, и есть много реальных форм этой абстракции. Ещё стоит помнить, что полиморфизм в ООП не ограничивается этим, перегрузка методов - также является полиморфизмом: много форм у одного метода.
Ещё раз, не разделяйте понятия ООП и не пытайтесь выбрать "что лучше" - все базовые принципы ООП - костяк, и только используя все кости сразу вы добьетесь красивого результата (конечно, меру знать нужно, так что злоупотреблять не стоит).
UPD. касательно вашего комментария:
если мы имеем супер-класс,но не абстрактный и наследуем от него другие классы,использующие его метод. можно ведь при этом делать супер-класс не абстрактным. например класс "фигура" и классы-наследники "квадрат", "круг".все будут иметь метод "рисовать".
abstract public class Shape { int Color; Coordinates StartPoint; abstract public void Draw(); }
class Point extends Shape { public void draw() { // Здесь рисуется точка } }
class Circle extends Shape { public void draw() { // Здесь рисуется круг } }
class Square extends Shape { public void draw() { // Здесь рисуется квадрат } }
Если суперкласс будет неабстрактным в данной ситуации, то либо придется писать реализацию draw() для класса Shape, которую не особо понятно как делать, либо же вообще Shape не будет ничего знать про метод draw() и тогда не получится сделать то, что описано ниже.
Зачем все это вообще нужно? Потому что когда-нибудь у вас будет много разных фигур, например, в массиве:
List shapes = shapes(); // Получим откуда-то его. shapes.forEach(Shape::draw);
И всё, мы отрисовали все квадраты, круги, точки. И для каждой фигуры вызвался свой метод draw.
Это так же ответ на вопрос:
а зачем нам выделять из нескольких классов общие черты и плодить еще один дополнительный класс?

Стилизировать первые 3 символа в тексте на css

Как на js или css стилизировать первые 3 символа в тексте? Нужно их скрыть или сделать прозрачными.

  • 1. Анкор (41)

  • Ответ

    Ну коли можно пользоваться скриптами теперь, то: Вариант на jq:
    $('input').on('click', function() { $('.cat-item a').each(function() { var text = $(this).text(); text = '' + text.substr(3, text.length); $(this).html(text); }) });

  • 1. hello!
  • 2. this
  • 3. is
  • 4. a test!

  • Как сделать эффект растущей цены (на подобие таймера)?

    На странице указана цена 126 рублей 00 копеек. Нужно эту цену каждую минуту увеличивать на 1 рубль. И также в течение минуты должны увеличиваться копейки, которые показывают, что цена растет. Как это можно реализовать? Если с рублями все понятно, используем setInterval и через каждые 60000мс пребавляем 1, то с копейками не могу сообразить.


    Ответ

    Решение с неточным таймером. var price = 126.00; updatePrice = function() { document.getElementById("price").innerHTML = XFormatPrice(price); price+=(100/60)/100; } updatePrice(); setInterval(updatePrice, 1000); function XFormatPrice(_number) { var format_string = '# руб.'; var r=parseFloat(_number) var exp10=Math.pow(10,2); r=Math.round(r*exp10)/exp10; rr=Number(r).toFixed(2).toString().split('.'); b=rr[0].replace(/(\d{1,3}(?=(\d{3})+(?:\.\d|\b)))/g,"\$1"+' '); r=(rr[1]?b+ ',' +rr[1]:b); return format_string.replace('#', r); }


    Заменить три блока if…else на более эстетичную запись

    Есть функция, которая принимает необязательный параметр: любое число. Все работает норм, пока это число !=0 function go(number){ let x = number || 10; console.log(x); } go()// вернет 10; go(20)// вернет 20; go(0)// вернет 10, а нужен 0;
    Проблема решается с помощью конструкции if..else..else, но для такой простой функции это слишком громоздко. Можно ли переписать как-то более эстетично?


    Ответ

    Попробуйте такой вариант:
    function go(number){ let x = number || number === 0 ? number : 10; console.log(x); } go()// вернет 10; go(20)// вернет 20; go(0)// вернет 0;

    Генератор случайных чисел без повторений C++. Получить диапазон 1..7 в случайном порядке

    Сижу, ломаю голову - и все никак не получается. Нужно создать массив из случайных чисел в диапозоне от 1 до 7 и чтобы там не было повторений. Я знаю, что есть много вариантов решения, но я выбрал следующий: при генерации числа заносить его в массив, а при генерации следующего пробегаться по массиву в поисках совпадения. Если найдено, то генерировать заново. Написал код, вроде бы все легко, но в ответе все равно проскальзывают повторения. Подскажите, где косяк?
    #include "pch.h" #include #include #include #include #include #include #include #include #include
    using namespace std;
    int main() { setlocale(LC_ALL, "Russian"); SetConsoleCP(1251); SetConsoleOutputCP(1251);
    srand(time(0));
    int a[7] = {0,0,0,0,0,0,0}; //создаю массив и заполняю нулями int random; //переменная для случайных чисел
    for (int i = 0; i < 7; i++) { //цикл создания случайных чисел random = 1 + rand() % 7; //создание случайного числа for (int j = 0; j < 7; j++) { //цикл проверки массива на совпадение if (random == a[j]) { //условие совпадения while (random == a[j]) { //цикл для создания нового случайного числа, пока оно не будет повторяться random = 1 + rand() % 7; } } else { continue; //если повтора нет, переходим к следующей итерации } } a[i] = random; // присваивание рандомного числа элементу массива }
    for (int k = 0; k < 7; k++) { //вывод массива на экран cout << a[k]; }
    cout << "Для выхода из консоли нажмите любую клавишу"; _getch(); return 0; }


    Ответ

    Для такого малого количества просто возьмите массив {1,2,3,4,5,6,7} и случайным образом его перетасуйте (например, с помощью shuffle). Будет быстрее и проще.
    А ваша проблема в том, что, проверив число на равенство одному a[j] и откорректировав, вы тут же об этом забываете при проверке на следующее значение j. Т.е. ваш цикл должен выглядеть примерно как
    for (int i = 0; i < 7; i++) { for(;;) { bool good = true; random = 1 + rand() % 7; for (int j = 0; j < 7; j++) { if (random == a[j]) { good = false; break; } } if (good) break; } a[i] = random; // присваивание рандомного числа элементу массива }

    Скачивание файла через urllib

    Подскажите, пожалуйста, как скачать файл средствами python (urllib) доступный по http и положить его рядом со скриптом?


    Ответ

    urllib -> urlopen - открывает файл по данной ему ссылке для чтения, далее можете его просто считать read(), открыть файл для бинарной записи, и записать в него то что прочитали по http. Пример скачивания эмблемки:
    import urllib logo = urllib.urlopen("http://www.google.com/images/srpr/logo3w.png").read() f = open("logo3w.png", "wb") f.write(logo) f.close()
    Также есть urllib.urlretrieve(url, [filname,[...]]) - по моему его предназначение именно для скачивания.
    import urllib destination = 'logo3w.png' url = 'http://www.google.com/images/srpr/logo3w.png' urllib.urlretrieve(url, destination)

    Явные отличия CMP от TEST

    Я начал изучать устройство одной программы недавно, целиком написанной на ассемблере (т.е никакие трансляторы в машинный не применялись, как в языках высокого уровня). Так вот, в ней я очень часто встречаю конструкции вида TEST AL, AL ; TEST EAX, EAX и т.д.
    Вот вопрос: почему применяется TEST, а не CMP? Да и какой смысл сравниваться с самим собой? Насколько я знаю, TEST использует логическое "И" (AND). Проясните, пожалуйста, в этих деталях я не особо просвещён.


    Ответ

    Команды вида test reg, reg используется для сравнения значения регистра с нулем. После этой операции флаги нуля, знака и чётности либо устанавливаются, либо сбрасываются. Почему не cmp reg, 0? Потому что в скомпилированном виде test reg, reg на целый байт короче, ну и по скорости наверное разница есть.
    test эквивалентен побитовому and, за исключением того, что модифицируются только флаги, но не операнды. Обычно это нужно, чтобы узнать, установлены ли определенные биты в регистре.
    А cmp эквивалентен обыкновенному вычитанию, но аналогично модифицирует флаги, не изменяя операнды.

    Присваивание символа const char *

    Добрый день, хочу расставить точки над «i» для себя.
    const char *v = "123d"; const char *c = "123"; c[2] = '5'; // Так нельзя c = v; c = "asdfas";
    Вопрос в следующем. Почему, когда мы const char * присваиваем строку, он присваивает, а символ — нет? Может, где-то для const char * перегружен оператор присваивания, и он ему присваивает новый адрес? А при присваивании литерала мы пытаемся записать по тому же адресу?


    Ответ

    Вопрос в следующем. Почему, когда мы const char * присваиваем строку, он присваивает, а символ — нет?
    Нужно различать константный указатель и указатель на константу.
    1) Указатель на константу: нельзя менять содержимое объекта, на который указывает указатель, но можно менять содержимое самого указателя (указатель — это переменная, содержащая адрес другой переменной).
    char array[] = "string"; const char * с = array; // Указатель на объект, который нельзя менять c[1] = 'a'; // Нельзя, т. к. меняется содержимое указываемого объекта с = "345"; // Можно, т. к. меняется значение самого указателя
    2) Константный указатель: можно менять содержимое объекта, но нельзя менять значение самого указателя. Проще говоря, указатель нельзя переназначить на другой объект, но сам указатель поменять можно.
    char array[] = "string"; char * const с = array; c[1] = 'a'; // можно с = "345"; // нельзя
    3) Константный указатель на константу: совокупность первых двух.
    const char * const с = "123";

    Поиск повторяющихся строк

    Привет. Возник вопрос: Есть файл 1 ~ 2GB и есть файл 2 ~ 1MB Вопрос - как наиболее быстро подсчитать кол-во вхождений строк из файла2 в файле1? К примеру файл1: Тест корова большая Флагман большая корова Трубадур золотистый Флагманский телефон N90 Логическое оформление Тест корова Бла бла стар Файл2: Трубадур Тест корова телефон Должно получиться: Трубадур 1 Тест корова 2 телефон 1 Файл 1 много больше файл2.


    Ответ

    Эффективным методом поиска нескольких подстрок одновременно в большом тексте является алгоритм Ахо-Корасика. Оригинальный fgrep (grep -F) использует этот алгоритм. GNU grep в этом случае использует Commentz-Walter алгоритм (объединение Ахо-Корасика и алгоритма поиска строки Бойера—Мура). ripgrep (rg) иногда работает быстрее GNU grep, используя SIMD алгоритм, называемый Teddy -- см. ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}
    Чтобы подсчитать найденные строки, можно использовать sort | uniq -c команду, предложенную @BOPOH в комментарии к вопросу. Ещё можно использовать словарь вместо сортировки:
    #!/bin/sh grep -Fof file2 file1 | perl -lne '$seen{$_}++ }{ while (my ($k,$v)=each %seen){print "$k $v"}'
    Измерения могут показать, какая из команд (sort+uniq или perl) быстрее в данном случае.
    Для сравнения можно посмотреть, сколько займёт Питон-скрипт без использования дополнительных пакетов (например, esm), которые реализуют Ахо-Корасик-подобные алгоритмы:
    #!/usr/bin/env python import re import sys from collections import Counter
    def main(): # load substrings from file2 with open('file2', 'rb') as file2: substrings = {line.strip() for line in file2} # unique lines substrings = filter(None, substrings) # filter blank lines substrings = sorted(substrings, key=len, reverse=True) # longest first pattern = b"|".join(map(re.escape, substrings)) find_substrings = re.compile(pattern).findall
    # count substrings in file1 counter = Counter() counter_update = counter.update # cache the lookup (for CPython) with open('file1', 'rb') as file1: for line in file1: counter_update(find_substrings(line))
    # write substrings frequencies write = getattr(sys.stdout, 'buffer', sys.stdout).write for substring, count in counter.most_common(): # most common first write(substring) write(b' ') write(str(count).encode()) write(b'
    ')
    main()
    Результат
    Тест корова 2 телефон 1 Трубадур 1
    Вот ещё вариант для сравнения, где строки в файле ищутся с помощью регулярного выражения и mmap
    #!/usr/bin/env python3 import re import sys from collections import Counter from mmap import ACCESS_READ, mmap from operator import methodcaller
    def generate_tokens(filename, pattern): with open(filename) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as mm: yield from re.finditer(pattern, mm)
    def main(): # load substrings from file2 with open('file2', 'rb') as file2: substrings = {line.strip() for line in file2} # unique lines substrings = filter(None, substrings) # filter blank lines substrings = sorted(substrings, key=len, reverse=True) # longest first pattern = b"|".join(map(re.escape, substrings))
    # count substrings in file1 file1_substrings = generate_tokens('file1', pattern) counter = Counter(map(methodcaller('group'), file1_substrings))
    # write substrings frequencies write = getattr(sys.stdout, 'buffer', sys.stdout).write for substring, count in counter.most_common(): # most common first write(substring) write(b' ') write(str(count).encode()) write(b'
    ')
    main()
    Работает на Windows, *nix. Код для Питон 3, но его можно легко адаптировать для Питон 2, если необходимо.