Я выдерживал сравнение для забавных различных языков для скорости в осуществлении следующей программы: поскольку я от 1 до 1 000 000 суммирую продукт i* (sqrt i)
Одна из моих реализаций (не единственная) создает список [1.. 1000000] и затем сворачивающийся с определенной функцией.
Программа хорошо работает и быстро в Haskell (даже когда с помощью foldl и не foldl'), но переполнения стека в OCaml и F#.
Вот код Haskell:
test = foldl (\ a b -> a + b * (sqrt b)) 0
create 0 = []
create n = n:(create (n-1))
main = print (test (create 1000000))
И вот OCaml один:
let test = List.fold_left (fun a b -> a +. (float_of_int b) *. (sqrt (float_of_int b))) 0. ;;
let rec create = function
| 0 -> []
| n -> n::(create (n-1)) ;;
print_float (test (create 1000000));;
Почему делает переполнения стека реализации OCaml/F#?
Я предлагаю вам взглянуть на Fortress Programming Language .
Это фактический язык программирования, и не псевдокод, но он был разработан как можно ближе к исполняемому псевдокоду. В частности, для того , чтобы намереваться синтаксиса они прочитали и проанализировали сотни CS и математических работ, курсов, книг и журналов, чтобы найти общие узоры использования для псевдокода и других вычислительных/математических обозначений.
Вы можете использовать все эти исследования, просто глядя на исходный код Fortress и абстрагируя вещи, которые вам не нужны, поскольку ваша целевая аудитория является человеком, в то время как Fortress's является компилятором.
Вот актуальный пример запуска кода Fortress из NAS (NASA Advanced Supercomputing) Сопряженный градиентный параллельный эталонный тест . Для получения удовольствия сравните спецификацию эталонного теста с реализацией в Fortress и обратите внимание на почти 1:1 соответствие. Также сравните реализацию в паре других языков, таких как C или Fortran, и обратите внимание, как они не имеют абсолютно никакого отношения к спецификации (а также часто на порядок длиннее спецификации).
Должен подчеркнуть: это не псевдокод, это действительный рабочий код Крепости! Пример кода крепости http ://StartFortresss.Sun.Com/Projects/Community/raw-attachment/wiki/Wiki/DiscovestionQuestions/NAS-CG.png
Изменить: ссылка «Выше пример кода» закрыта. Возможно, подобный пример можно найти здесь: https://umbilicus.wordpress.com/2009/10/16/fortress-parallel-by-default/
-121--1421877-Из вашего вопроса я думаю, что вы хотите отобразить форму для редактирования записи человека в базе данных.
Код контроллера
// Normally data object is retrieved from the database
// This is just to simplify the code
$person = array('id' => 1, 'name' => 'stephenc');
// Pass to the view
$this->load->view('my_view_name', array('person' => $person));
Просмотр кода
<?php echo form_label('Your name: ', 'name'); ?>
<?php echo form_input(array('name' => 'name', 'value' => $person['name'])); ?>
Не забудьте повторить то, что возвращается от form_label и form_input. Это может быть то, где ты ошибаешься.
-121--4998062- Код Haskell для create
вычисляет список лениво, т.е. так как элементы необходимы для foldl
. Весь список не нужен сразу.
Напротив, F # и OCaml строго вычисляют список create
, т.е. код пытается создать весь список за один раз, прежде чем передать его в кратное _ слева
.
Одна из возможностей F # состоит в использовании выражения seq
в функции create
: список генерируется лениво в том же пути, что и Haskell. (Я не уверен, что OCaml имеет эквивалентную функцию.)
Другой способ исправить код так, чтобы он работал в F# - это использовать выражения последовательности, которые также генерируются лениво и так, чтобы не вызывать StackOverflow
(это не будет работать в OCaml, потому что выражения последовательности специфичны для F#). Вот код:
let test nums = Seq.fold (fun a b ->
a + (float b) * (sqrt (float b))) 0.0 nums
let rec create count = seq {
match count with
| 0 -> do ()
| n -> yield n
yield! create (n-1) }
printf "%f" (test (create 1000000));;
Я не уверен в производительности, однако компилятор определенно делает оптимизацию в функции create
, так что итерация по сгенерированной последовательности должна быть достаточно быстрой. Однако цель выражений последовательности - это в основном читабельность (поскольку F# не является чистым языком, он дает вам много места для ручной оптимизации, если она вам действительно нужна, в отличие от Haskell, который должен оптимизировать чистый код, который вы пишете). В любом случае, если у вас есть несколько тестов, вы можете попробовать!
Во-первых, если вы проводите сравнение производительности для числовых данных, списки - не лучший выбор. Попробуйте такой пакет, как vector , для быстрых массивов.
Обратите внимание, что благодаря слиянию циклов вы можете добиться большего в Haskell. Написав функцию create в виде перечисления, компилятор может объединить этап создания и цикл сворачивания в один цикл, который не выделяет промежуточных структур данных. Возможность такого общего слияния уникальна для GHC Haskell.
Я воспользуюсь векторной библиотекой (объединение потоков на основе циклов):
import qualified Data.Vector as V
test = V.foldl (\ a b -> a + b * sqrt b) 0
create n = (V.enumFromTo 1 n)
main = print (test (create 1000000))
Раньше, с вашим кодом, компилятор не мог удалить все списки, и мы получили внутренний цикл вроде:
$wlgo :: Double# -> [Double] -> Double#
$wlgo =
\ (ww_sww :: Double#) (w_swy :: [Double]) ->
case w_swy of _ {
[] -> ww_sww;
: x_aoY xs_aoZ ->
case x_aoY of _ { D# x1_aql ->
$wlgo
(+##
ww_sww (*## x1_aql (sqrtDouble# x1_aql)))
xs_aoZ
}
}
$wcreate :: Double# -> [Double]
$wcreate =
\ (ww_swp :: Double#) ->
case ==## ww_swp 0.0 of _ {
False ->
:
@ Double
(D# ww_swp)
($wcreate (-## ww_swp 1.0));
True -> [] @ Double
}
Обратите внимание на то, что существует два цикла: создание, генерирующее (ленивый) список, и свертка, использующая его. Из-за лени стоимость этого списка невелика, поэтому он работает достойно:
$ time ./C
4.000004999999896e14
./C 0.06s user 0.00s system 98% cpu 0.058 total
Однако при объединении мы получаем вместо этого только один цикл!
main_$s$wfoldlM_loop :: Double# -> Double# -> Double#
main_$s$wfoldlM_loop =
\ (sc_sYc :: Double#) (sc1_sYd :: Double#) ->
case <=## sc_sYc 1000000.5 of _ {
False -> sc1_sYd;
True ->
main_$s$wfoldlM_loop
(+## sc_sYc 1.0)
(+##
sc1_sYd (*## sc_sYc (sqrtDouble# sc_sYc)))
GHC сократил наши шаги создания и тестирования до единого цикла без использования списков. Всего 2 двойных регистра в регистрах. А с вдвое меньшим количеством циклов он работает почти в два раза быстрее:
$ ghc D.hs -Odph -fvia-C -optc-O3 -optc-march=native -fexcess-precision --make
$ time ./D
4.000005000001039e14
./D 0.04s user 0.00s system 95% cpu 0.038 total
Это хороший пример того, насколько мощными являются гарантии чистоты - компилятор может быть очень агрессивным изменение кода.
В таком виде ваша функция create
не является хвостовой рекурсивной. Вы можете переписать ее в хвостовую рекурсивную форму, которая не вызывает переполнения стека:
let rec create n l =
match n with
| 0 -> l
| n -> create (n-1) (n::l)
print_float (test (create 1000000 []));;
Программа отлично и быстро работает в Haskell (даже при использовании foldl, а не foldl '), но в OCaml и F # происходит переполнение стека.
Ваш Haskell использует ленивые списки, но ваш OCaml / F # использует строгие списки, поэтому ваши программы несопоставимы.
FWIW, решение с использованием последовательностей по запросу в F #:
Seq.sumBy (fun i -> let i = float i in i * sqrt i) (seq {1..1000000})