0

負数と正数のシーケンスのリストが与えられた場合、foldr を使用してそれらを負数と正数のシーケンスに分割するにはどうすればよいですか?

たとえば、[1,2,3,-1,-2,-3,1,2,3] [[1,2,3],[-1,-2,-3],[1, 2,3]]

いくつかの疑問

すでに比較した前のパーティションが、現在比較しているパーティションと同じ符号であるかどうかを知るにはどうすればよいですか?

要素をリストに追加するにはどうすればよいですか? [x]:y のようなものを試しましたが、得られたのは各要素をリストとして連結したものであり、結果ではありません。

私が現在持っているのはこれです

foldr (\ x y -> if  x >= 0  then [x]:y else y ) [[]] 

どちらが間違っていますか

事前に助けてくれてありがとう。

4

6 に答える 6

5

私はgroupBy代わりにの使用法を2番目に使用します。ただし、0 は数学では正の数とは見なされないという点を指摘したいと思います。そして、これまでのところ他の回答が言及されていないように、型クラスのものはNumすべて実装する必要がありsignum、これはそれに与えられた数値の符号を返します。

import Data.List     (groupBy)
import Data.Function (on) -- Can evade a lambda

signGroup :: (Num a) => [a] -> [[a]]
signGroup = groupBy ((==) `on` signum)

使用例:

> signGroup [1,2,3,0,0,-1,-2,1,2,0,3,4,-1]
[[1,2,3],[0,0],[-1,-2],[1,2],[0],[3,4],[-1]]
于 2012-10-11T16:28:23.243 に答える
2

リスト内の各数値の符号をその後の数値の符号と比較したいとします。x符号が同じ場合は、後続のリストと同じリストに入れたいと考えています。そうでない場合は、新しい内部リストを開始します。

そう

combine x [[]] = [[x]]
combine x wss@(yys@(y:_):zss)
  | sameSign x y = (x:yys) : zss
  | otherwise    = [x] : wss

あなたが望むことをします(の実装が与えられた場合sameSign)。しかし、それはあまり効率的ではありません (無限リストではまったく機能しません)。使用する方程式を知るには、後の部分をx構築する必要があり、それは入力リストの最後に最初に到達し、次に 1 つに到達する必要があることを意味するためです。リストをさかのぼる必要があります。

解決策は怠惰です。2 番目の引数を検査する前に、結果の構築を開始する必要があります。

combine x wss = (x:ssx) : rest
  where
    (ssx:rest) = case wss of
                   [[]] -> [] : []
                   (yys@(y:ys) : zss)
                       | sameSign x y -> yys : zss
                       | otherwise    -> [] : wss

それで

foldr combine [[]] input

たとえば、

sameSign x y
    | x < 0     = y < 0
    | otherwise = y >= 0

(もちろん、使用するgroupBy方が短くて簡単ですが、それは使用しませんfoldr:)

于 2012-10-11T11:52:36.020 に答える
1

ここでは、もう少し複雑なアキュムレータが必要です。

data Sign = Neg | Zero | Pos

signGroup :: [Integer] -> [[Integer]]
signGroup xs = case
  foldr
  (\x (sign, ps, ns, ys) ->
    -- x    - current element
    -- sign - sign of the prev. group
    -- ps   - positive numbers in the current group
    -- ns   - negative numbers in the current group
    -- ys   - final list
    if x >= 0
    then case sign of
      Neg  -> (Pos, x : ps, [], ns : ys)
      Zero -> (Pos, x : ps, [], ys)
      Pos  -> (Pos, x : ps, [], ys)
    else case sign of
      Neg  -> (Neg, [], x : ns, ys)
      Zero -> (Neg, [], x : ns, ys)
      Pos  -> (Neg, [], x : ns, ps : ys))
  (Zero, [], [], [])
  xs
 of
    (_, [], [], ys) -> ys
    (_, [], ns, ys) -> ns : ys
    (_, ps, [], ys) -> ps : ys
    (_, ps, ns, ys) -> ps : ns : ys -- <- unreachable

-- signGroup [1,2,3,-1,-2,-3,1,2,3]
-- => [[1,2,3],[-1,-2,-3],[1,2,3]]
于 2012-10-12T23:18:54.363 に答える
0

foldrを使用して可能な答えを提供します。これは非常に効率的ではなく(aとbが同じ符号であることを示すためにa * b> = 0を使用しています)、無限のリストで機能しないため、これを使用する必要があると言っているわけではありません。

combine = foldr f []
    where
        f a [] = [[a]]
        f a rest@((x:xs):ys) | x*a >= 0 = (a:x:xs):ys
                             | otherwise = [a]:rest
于 2012-10-11T13:38:51.533 に答える
0

仕事に最適なツールを使用してください。この場合、ジョブに最適なツールはgroupBy.

groupByリストの 2 つのメンバーを関数に渡すので、それらが同じ符号を持っているかどうかを簡単に確認できます。

(foldr本当にしたい場合は の観点で書くことができます ---groupByの観点で書くことができますfoldrが、標準の実装ではそうではありません --- 必要以上に複雑になるだけです。)

于 2012-10-11T11:52:06.027 に答える
0

groupByfromなどを使用して、必要なものを取得できますData.List

import Data.List (groupBy)
let result = groupBy (\ x y -> x*y>0) [1,2,3,-1,-2,-3,1,2,3]

個別に扱いたくない場合は、チェックの代わりに0Daniel Fischer の関数などを使用します。sameSign

于 2012-10-11T11:52:24.400 に答える