Когда речь заходит о неправильном, или плохом коде, то лично я в первую очередь вспоминаю свой опыт написания лабораторных на Фортране. Это удивительно, но в интернете на каждом шагу можно наблюдать невиданной изощренности вредные и громоздкие конструкции на этом языке. Сниппеты на Stackoverflow, библиотеки, решения задач на форумах – очень часто люди делают из Фортрана что-то не то.

Пример сишности в коде на Фортран

Я продемонстрирую ситуацию наглядно на примере задачи о подсчете вхождений подстроки в строку. На сайте Rosettacode.org висит такое решение:

program Example
  implicit none
  integer :: n

  n = countsubstring("the three truths", "th")
  write(*,*) n
  n = countsubstring("ababababab", "abab")
  write(*,*) n
  n = countsubstring("abaabba*bbaba*bbab", "a*b")
  write(*,*) n

contains

function countsubstring(s1, s2) result(c)
  character(*), intent(in) :: s1, s2
  integer :: c, p, posn

  c = 0
  if(len(s2) == 0) return
  p = 1
  do
    posn = index(s1(p:), s2)
    if(posn == 0) return
    c = c + 1
    p = p + posn + len(s2)
  end do
end function
end program

Разберем функцию countsubstring, непосредственно решающую задачу:

function countsubstring(s1, s2) result(c)
  character(*), intent(in) :: s1, s2
  integer :: c, p, posn

  c = 0
  if(len(s2) == 0) return
  p = 1
  do
    posn = index(s1(p:), s2)
    if(posn == 0) return
    c = c + 1
    p = p + posn + len(s2)
  end do
end function

Здесь c – это количество вхождений, s1 – исходная строка, s2 – искомая подстрока, posn – текущая позиция, p – позиция, с которой начнется следующий поиск (в Фортране нумерация массивов идет от единицы). В данном примере автор все-таки использовал пару внутренних функций Фортрана (index для поиска позиции следующего вхождения и len для поиска длины строки), но подход в целом сишный.

Как должно быть

Для более грамотного решения задачи нам очень пригодятся такие Фортрановские фишки, как параллельный цикл do concurrent и сечения массивов. План такой: каждая итерация цикла будет независимо и параллельно брать назначенное ей сечение исходной строки и проверять, совпало ли оно с искомой подстрокой. Также сделаем функцию чистой и оставим старые наименования переменных везде, где это возможно. Готовое решение:

pure function countsubstring(s1, s2) result(c)
  character(*), intent(in) :: s1, s2
  integer                  :: N, M, i, c

  N = len(s1)
  M = len(s2)
  c = 0

  if (M == 0 .or. N == 0) return

  do concurrent (i = 1:(N - M + 1))
    if (s1(i:(i+M-1)) == s2) c = c + 1
  enddo

end function

Кстати, исправленный алгоритм нашел на 2 вхождения больше, чем исходный в случае с countsubstring("ababababab", "abab"), и это правильно. В общем, когда пишете на Фотране, пишите на Фортране, это и проще, и эффективнее :)